CVE-2016-5356: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: wiretap/cosine.c in the CoSine file parser in Wireshark 1.12.x before 1.12.12 and 2.x before 2.0.4 mishandles sscanf unsigned-integer processing, which allows remote attackers to cause a denial of service (application crash) via a crafted file.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 5.9
Published: 2016-08-07T16:59Z
Repository: https://github.com/wireshark/wireshark
Language: C
Files changed: wiretap/cosine.c
Commit date: 2016-04-29 22:51:41 +0000
Commit message: Fix packet length handling.

Treat the packet length as unsigned - it shouldn't be negative in the
file.  If it is, that'll probably cause the sscanf to fail, so we'll
report the file as bad.

Check it against WTAP_MAX_PACKET_SIZE to make sure we don't try to
allocate a huge amount of memory, just as we do in other file readers.

Use the now-validated packet size as the length in
ws_buffer_assure_space(), so we are certain to have enough space, and
don't allocate too much space.

Merge the header and packet data parsing routines while we're at it.

Bug: 12395
Change-Id: Ia70f33b71ff28451190fcf144c333fd1362646b2
Reviewed-on: https://code.wireshark.org/review/15172
Reviewed-by: Guy Harris <guy@alum.mit.edu>

Diff: diff --git a/wiretap/cosine.c b/wiretap/cosine.c
index 5163cb4eef2..5d48b912e83 100644
--- a/wiretap/cosine.c
+++ b/wiretap/cosine.c
@@ -155,24 +155,20 @@
 #define COSINE_REC_MAGIC_STR2	COSINE_HDR_MAGIC_STR2
 
 #define COSINE_HEADER_LINES_TO_CHECK	200
 #define COSINE_LINE_LENGTH		240
 
-#define COSINE_MAX_PACKET_LEN	65536
-
 static gboolean empty_line(const gchar *line);
 static gint64 cosine_seek_next_packet(wtap *wth, int *err, gchar **err_info,
 	char *hdr);
 static gboolean cosine_check_file_type(wtap *wth, int *err, gchar **err_info);
 static gboolean cosine_read(wtap *wth, int *err, gchar **err_info,
 	gint64 *data_offset);
 static gboolean cosine_seek_read(wtap *wth, gint64 seek_off,
 	struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
-static int parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
-	int *err, gchar **err_info);
-static gboolean parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr,
-	int pkt_len, Buffer* buf, int *err, gchar **err_info);
+static int parse_cosine_packet(FILE_T fh, struct wtap_pkthdr *phdr, Buffer* buf,
+	char *line, int *err, gchar **err_info);
 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
 	guint byte_offset);
 
 /* Returns TRUE if the line appears to be an empty line. Otherwise it
    returns FALSE. */
@@ -285,35 +281,28 @@ wtap_open_return_val cosine_open(wtap *wth, int *err, gchar **err_info)
 /* Find the next packet and parse it; called from wtap_read(). */
 static gboolean cosine_read(wtap *wth, int *err, gchar **err_info,
     gint64 *data_offset)
 {
 	gint64	offset;
-	int	pkt_len;
 	char	line[COSINE_LINE_LENGTH];
 
 	/* Find the next packet */
 	offset = cosine_seek_next_packet(wth, err, err_info, line);
 	if (offset < 0)
 		return FALSE;
 	*data_offset = offset;
 
-	/* Parse the header */
-	pkt_len = parse_cosine_rec_hdr(&wth->phdr, line, err, err_info);
-	if (pkt_len == -1)
-		return FALSE;
-
-	/* Convert the ASCII hex dump to binary data */
-	return parse_cosine_hex_dump(wth->fh, &wth->phdr, pkt_len,
-	    wth->frame_buffer, err, err_info);
+	/* Parse the header and convert the ASCII hex dump to binary data */
+	return parse_cosine_packet(wth->fh, &wth->phdr, wth->frame_buffer,
+	    line, err, err_info);
 }
 
 /* Used to read packets in random-access fashion */
 static gboolean
 cosine_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *phdr,
 	Buffer *buf, int *err, gchar **err_info)
 {
-	int	pkt_len;
 	char	line[COSINE_LINE_LENGTH];
 
 	if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
 		return FALSE;
 
@@ -323,67 +312,75 @@ cosine_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *phdr,
 			*err = WTAP_ERR_SHORT_READ;
 		}
 		return FALSE;
 	}
 
-	/* Parse the header */
-	pkt_len = parse_cosine_rec_hdr(phdr, line, err, err_info);
-	if (pkt_len == -1)
-		return FALSE;
-
-	/* Convert the ASCII hex dump to binary data */
-	return parse_cosine_hex_dump(wth->random_fh, phdr, pkt_len, buf, err,
+	/* Parse the header and convert the ASCII hex dump to binary data */
+	return parse_cosine_packet(wth->random_fh, phdr, buf, line, err,
 	    err_info);
 }
 
 /* Parses a packet record header. There are two possible formats:
     1) output to a control blade with date and time
         2002-5-10,20:1:31.4:  l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
     2) output to PE without date and time
         l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0] */
-static int
-parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
-     int *err, gchar **err_info)
+static gboolean
+parse_cosine_packet(FILE_T fh, struct wtap_pkthdr *phdr, Buffer *buf,
+    char *line, int *err, gchar **err_info)
 {
 	union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
 	int	num_items_scanned;
-	int	yy, mm, dd, hr, min, sec, csec, pkt_len;
+	int	yy, mm, dd, hr, min, sec, csec;
+	guint	pkt_len;
 	int	pro, off, pri, rm, error;
 	guint	code1, code2;
 	char	if_name[COSINE_MAX_IF_NAME_LEN] = "", direction[6] = "";
 	struct	tm tm;
+	guint8 *pd;
+	int	i, hex_lines, n, caplen = 0;
 
 	if (sscanf(line, "%4d-%2d-%2d,%2d:%2d:%2d.%9d:",
 		   &yy, &mm, &dd, &hr, &min, &sec, &csec) == 7) {
 		/* appears to be output to a control blade */
 		num_items_scanned = sscanf(line,
-		   "%4d-%2d-%2d,%2d:%2d:%2d.%9d: %5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
+		   "%4d-%2d-%2d,%2d:%2d:%2d.%9d: %5s (%127[A-Za-z0-9/:]), Length:%9u, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
 			&yy, &mm, &dd, &hr, &min, &sec, &csec,
 				   direction, if_name, &pkt_len,
 				   &pro, &off, &pri, &rm, &error,
 				   &code1, &code2);
 
 		if (num_items_scanned != 17) {
 			*err = WTAP_ERR_BAD_FILE;
 			*err_info = g_strdup("cosine: purported control blade line doesn't have code values");
-			return -1;
+			return FALSE;
 		}
 	} else {
 		/* appears to be output to PE */
 		num_items_scanned = sscanf(line,
-		   "%5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
+		   "%5s (%127[A-Za-z0-9/:]), Length:%9u, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
 				   direction, if_name, &pkt_len,
 				   &pro, &off, &pri, &rm, &error,
 				   &code1, &code2);
 
 		if (num_items_scanned != 10) {
 			*err = WTAP_ERR_BAD_FILE;
 			*err_info = g_strdup("cosine: header line is neither control blade nor PE output");
-			return -1;
+			return FALSE;
 		}
 		yy = mm = dd = hr = min = sec = csec = 0;
 	}
+	if (pkt_len > WTAP_MAX_PACKET_SIZE) {
+		/*
+		 * Probably a corrupt capture file; don't blow up trying
+		 * to allocate space for an immensely-large packet.
+		 */
+		*err = WTAP_ERR_BAD_FILE;
+		*err_info = g_strdup_printf("cosine: File has %u-byte packet, bigger than maximum of %u",
+		    pkt_len, WTAP_MAX_PACKET_SIZE);
+		return FALSE;
+	}
 
 	phdr->rec_type = REC_TYPE_PACKET;
 	phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
 	tm.tm_year = yy - 1900;
 	tm.tm_mon = mm - 1;
@@ -428,25 +425,12 @@ parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
 	pseudo_header->cosine.off = off;
 	pseudo_header->cosine.pri = pri;
 	pseudo_header->cosine.rm = rm;
 	pseudo_header->cosine.err = error;
 
-	return pkt_len;
-}
-
-/* Converts ASCII hex dump to binary data. Returns TRUE on success,
-   FALSE if any error is encountered. */
-static gboolean
-parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr, int pkt_len,
-    Buffer* buf, int *err, gchar **err_info)
-{
-	guint8 *pd;
-	gchar	line[COSINE_LINE_LENGTH];
-	int	i, hex_lines, n, caplen = 0;
-
 	/* Make sure we have enough room for the packet */
-	ws_buffer_assure_space(buf, COSINE_MAX_PACKET_LEN);
+	ws_buffer_assure_space(buf, pkt_len);
 	pd = ws_buffer_start_ptr(buf);
 
 	/* Calculate the number of hex dump lines, each
 	 * containing 16 bytes of data */
 	hex_lines = pkt_len / 16 + ((pkt_len % 16) ? 1 : 0);
@@ -471,11 +455,10 @@ parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr, int pkt_len,
 	}
 	phdr->caplen = caplen;
 	return TRUE;
 }
 
-
 /* Take a string representing one line from a hex dump and converts
  * the text to binary data. We place the bytes in the buffer at the
  * specified offset.
  *
  * Returns number of bytes successfully read, -1 if bad.  */

Vulnerable code: #define COSINE_MAX_PACKET_LEN	65536

static int parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
	int *err, gchar **err_info);
static gboolean parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr,
	int pkt_len, Buffer* buf, int *err, gchar **err_info);
	int	pkt_len;
	/* Parse the header */
	pkt_len = parse_cosine_rec_hdr(&wth->phdr, line, err, err_info);
	if (pkt_len == -1)
		return FALSE;

	/* Convert the ASCII hex dump to binary data */
	return parse_cosine_hex_dump(wth->fh, &wth->phdr, pkt_len,
	    wth->frame_buffer, err, err_info);
	int	pkt_len;
	/* Parse the header */
	pkt_len = parse_cosine_rec_hdr(phdr, line, err, err_info);
	if (pkt_len == -1)
		return FALSE;

	/* Convert the ASCII hex dump to binary data */
	return parse_cosine_hex_dump(wth->random_fh, phdr, pkt_len, buf, err,
static int
parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
     int *err, gchar **err_info)
	int	yy, mm, dd, hr, min, sec, csec, pkt_len;
		   "%4d-%2d-%2d,%2d:%2d:%2d.%9d: %5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
			return -1;
		   "%5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
			return -1;
	return pkt_len;
}

/* Converts ASCII hex dump to binary data. Returns TRUE on success,
   FALSE if any error is encountered. */
static gboolean
parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr, int pkt_len,
    Buffer* buf, int *err, gchar **err_info)
{
	guint8 *pd;
	gchar	line[COSINE_LINE_LENGTH];
	int	i, hex_lines, n, caplen = 0;

	ws_buffer_assure_space(buf, COSINE_MAX_PACKET_LEN);

Fixed code: static int parse_cosine_packet(FILE_T fh, struct wtap_pkthdr *phdr, Buffer* buf,
	char *line, int *err, gchar **err_info);
	/* Parse the header and convert the ASCII hex dump to binary data */
	return parse_cosine_packet(wth->fh, &wth->phdr, wth->frame_buffer,
	    line, err, err_info);
	/* Parse the header and convert the ASCII hex dump to binary data */
	return parse_cosine_packet(wth->random_fh, phdr, buf, line, err,
static gboolean
parse_cosine_packet(FILE_T fh, struct wtap_pkthdr *phdr, Buffer *buf,
    char *line, int *err, gchar **err_info)
	int	yy, mm, dd, hr, min, sec, csec;
	guint	pkt_len;
	guint8 *pd;
	int	i, hex_lines, n, caplen = 0;
		   "%4d-%2d-%2d,%2d:%2d:%2d.%9d: %5s (%127[A-Za-z0-9/:]), Length:%9u, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
			return FALSE;
		   "%5s (%127[A-Za-z0-9/:]), Length:%9u, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
			return FALSE;
	if (pkt_len > WTAP_MAX_PACKET_SIZE) {
		/*
		 * Probably a corrupt capture file; don't blow up trying
		 * to allocate space for an immensely-large packet.
		 */
		*err = WTAP_ERR_BAD_FILE;
		*err_info = g_strdup_printf("cosine: File has %u-byte packet, bigger than maximum of %u",
		    pkt_len, WTAP_MAX_PACKET_SIZE);
		return FALSE;
	}
	ws_buffer_assure_space(buf, pkt_len);