CVE-2024-24479 (published 2024-02-21T19:15Z)

A Buffer Overflow in Wireshark before 4.2.0 allows a remote attacker to cause a denial of service via the wsutil/to_str.c, and format_fractional_part_nsecs components. NOTE: this is disputed by the vendor because neither release 4.2.0 nor any other release was affected.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/wireshark/wireshark
commit_details: repo_url: https://github.com/wireshark/wireshark
author: Guy Harris
author_date: 2023-09-12 01:23:35-07:00
msg: Various enhancements and fixes to format_fractional_part_nsecs().

Have format_fractional_part_nsecs() return the number of bytes
formatted.  This matches what snprintf() and strlcpy() do, for the
benefit of routines constructing a string in a fixed-length buffer with
multiple calls, one of which is format_fractional_part_nsecs().

Have format_fractional_part_nsecs() check for a count of nanoseconds
greatre than or equal to 1 billion, and report that specially.

Fix places wher it used the bufer pointer and size of the buffer, rather
than the current position in the buffer and the remaining space in the
buffer, to append to the buffer and make sure doing so doesn't overrun
the buffer.

Rename some variable names (rename buf_remaining to just remaining,
rename num_chars to num_bytes as it counts bytes).

Fix some comments.
repository: repo_url: https://github.com/wireshark/wireshark
repo_name: wireshark/wireshark
description: Read-only mirror of Wireshark's Git repository at https://gitlab.com/wireshark/wireshark. ⚠️ GitHub won't let us disable pull requests. ⚠️ THEY WILL BE IGNORED HERE ⚠️ Upload them at GitLab instead.
repo_language: C
owner: wireshark
file_changes: filename: to_str.c
change_type: ModificationType.MODIFY
diff: @@ -653,12 +653,17 @@ eui64_to_str(wmem_allocator_t *scope, const guint64 ad) {
 #define CHARS_NANOSECONDS	9	/* 000000001 */
 
 /*
- * Format the fractonal part of a time, with the specified precision)
+ * Format the fractional part of a time, with the specified precision.
+ * Returns the number of bytes formatted.
  */
-void
+int
 format_fractional_part_nsecs(gchar *buf, size_t buflen, guint32 nsecs, const char *decimal_point, int precision)
 {
+	gchar *ptr;
+	size_t remaining;
+	int num_bytes;
 	gsize decimal_point_len;
+	guint32 frac_part;
 	gint8 num_buf[CHARS_NANOSECONDS];
 	gint8 *num_end = &num_buf[CHARS_NANOSECONDS];
 	gint8 *num_ptr;
@@ -671,9 +676,33 @@ format_fractional_part_nsecs(gchar *buf, size_t buflen, guint32 nsecs, const cha
 		 * No room in the buffer for anything, including
 		 * a terminating '\0'.
 		 */
-		return;
+		return 0;
+	}
+
+	/*
+	 * If the fractional part is >= 1, don't show it as a
+	 * fractional part.
+	 */
+	if (nsecs >= 1000000000U) {
+		num_bytes = snprintf(buf, buflen, "%s(%u nanoseconds)",
+		    decimal_point, nsecs);
+		if ((unsigned int)num_bytes >= buflen) {
+			/*
+			 * That filled up or would have overflowed
+			 * the buffer.  Nothing more to do; return
+			 * the remaining space in the buffer, minus
+			 * one byte for the terminating '\0',* as
+			 * that's the number of bytes we copied.
+			 */
+			return (int)(buflen - 1);
+		}
+		return num_bytes;
 	}
 
+	ptr = buf;
+	remaining = buflen;
+	num_bytes = 0;
+
 	/*
 	 * Copy the decimal point.
 	 */
@@ -681,12 +710,16 @@ format_fractional_part_nsecs(gchar *buf, size_t buflen, guint32 nsecs, const cha
 	if (decimal_point_len >= buflen) {
 		/*
 		 * The decimal point didn't fit in the buffer
-		 * and was truncated.  Nothing more to do.
+		 * and was truncated.  Nothing more to do;
+		 * return the remaining space in the buffer,
+		 * minus one byte for the terminating '\0',
+		 * as that's the number of bytes we copied.
 		 */
-		return;
+		return (int)(buflen - 1);
 	}
-	buf += decimal_point_len;
-	buflen -= decimal_point_len;
+	ptr += decimal_point_len;
+	remaining -= decimal_point_len;
+	num_bytes += decimal_point_len;
 
 	/*
 	 * Fill in num_buf with the nanoseconds value, padded with
@@ -709,72 +742,63 @@ format_fractional_part_nsecs(gchar *buf, size_t buflen, guint32 nsecs, const cha
 		/*
 		 * Scale down to units of 1/10 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 100000000, precision);
+		frac_part = nsecs / 100000000U;
 		break;
 
 	case 2:
 		/*
 		 * Scale down to units of 1/100 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 10000000, precision);
+		frac_part = nsecs / 10000000U;
 		break;
 
 	case 3:
 		/*
 		 * Scale down to units of 1/1000 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 1000000, precision);
+		frac_part = nsecs / 1000000U;
 		break;
 
 	case 4:
 		/*
 		 * Scale down to units of 1/10000 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 100000, precision);
+		frac_part = nsecs / 100000U;
 		break;
 
 	case 5:
 		/*
 		 * Scale down to units of 1/100000 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 10000, precision);
+		frac_part = nsecs / 10000U;
 		break;
 
 	case 6:
 		/*
 		 * Scale down to units of 1/1000000 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 1000, precision);
+		frac_part = nsecs / 1000U;
 		break;
 
 	case 7:
 		/*
 		 * Scale down to units of 1/10000000 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 100, precision);
+		frac_part = nsecs / 100U;
 		break;
 
 	case 8:
 		/*
 		 * Scale down to units of 1/100000000 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end,
-		    nsecs / 10, precision);
+		frac_part = nsecs / 10U;
 		break;
 
 	case 9:
 		/*
 		 * We're already in units of 1/1000000000 second.
 		 */
-		num_ptr = uint_to_str_back_len(num_end, nsecs,
-		    precision);
+		frac_part = nsecs;
 		break;
 
 	default:
@@ -782,31 +806,37 @@ format_fractional_part_nsecs(gchar *buf, size_t buflen, guint32 nsecs, const cha
 		break;
 	}
 
+	num_ptr = uint_to_str_back_len(num_end, frac_part, precision);
+
 	/*
 	 * The length of the string that we want to copy to the buffer
 	 * is the minimum of:
 	 *
 	 *    the length of the digit string;
-	 *    the size of the buffer, minus 1 for the terminating
-	 *      '\0'.
+	 *    the remaining space in the buffer, minus 1 for the
+	 *      terminating '\0'.
 	 */
-	num_len = MIN((size_t)(num_end - num_ptr), buflen - 1);
+	num_len = MIN((size_t)(num_end - num_ptr), remaining - 1);
 	if (num_len == 0) {
 		/*
 		 * Not enough room to copy anything.
+		 * Return the number of bytes we've generated.
 		 */
-		return;
+		return num_bytes;
 	}
 
 	/*
 	 * Copy over the fractional part.
 	 */
-	memcpy(buf, num_ptr, num_len);
+	memcpy(ptr, num_ptr, num_len);
+	ptr += num_len;
+	num_bytes += num_len;
 
 	/*
 	 * '\0'-terminate it.
 	 */
-	*(buf + num_len) = '\0';
+	*ptr = '\0';
+	return num_bytes;
 }
 
 void
@@ -892,8 +922,8 @@ format_nstime_as_iso8601(gchar *buf, size_t buflen, const nstime_t *ns,
 {
 	struct tm tm, *tmp;
 	gchar *ptr;
-	size_t buf_remaining;
-	int num_chars;
+	size_t remaining;
+	int num_bytes;
 
 	if (local)
 		tmp = ws_localtime_r(&ns->secs, &tm);
@@ -904,8 +934,8 @@ format_nstime_as_iso8601(gchar *buf, size_t buflen, const nstime_t *ns,
 		return;
 	}
 	ptr = buf;
-	buf_remaining = buflen;
-	num_chars = snprintf(ptr, buf_remaining,
+	remaining = buflen;
+	num_bytes = snprintf(ptr, remaining,
 	    "%04d-%02d-%02d %02d:%02d:%02d",
 	    tmp->tm_year + 1900,
 	    tmp->tm_mon + 1,
@@ -913,36 +943,32 @@ format_nstime_as_iso8601(gchar *buf, size_t buflen, const nstime_t *ns,
 	    tmp->tm_hour,
 	    tmp->tm_min,
 	    tmp->tm_sec);
-	if (num_chars < 0) {
+	if (num_bytes < 0) {
 		/*
+		 * That got an error.
 		 * Not much else we can do.
 		 */
 		snprintf(buf, buflen, "snprintf() failed");
 		return;
 	}
-	if ((unsigned int)num_chars >= buf_remaining) {
+	if ((unsigned int)num_bytes >= remaining) {
 		/*
-		 * Either that got an error (num_chars < 0) or it
-		 * filled up or would have overflowed the buffer
-		 * (num_chars >= buf_remaining).
+		 * That filled up or would have overflowed the buffer.
 		 * Nothing more we can do.
 		 */
 		return;
 	}
-	ptr += num_chars;
-	buf_remaining -= num_chars;
+	ptr += num_bytes;
+	remaining -= num_bytes;
 
-	if (precision == 0) {
+	if (precision != 0) {
 		/*
-		 * Seconds precision, so no nanosecond.
+		 * Append the fractional part.
+		 * Get the nsecs as a 32-bit unsigned value, as it should
+		 * never be negative, so we treat it as unsigned.
 		 */
-		return;
+		format_fractional_part_nsecs(ptr, remaining, (guint32)ns->nsecs, decimal_point, precision);
 	}
-
-	/*
-	 * Append the fractional part.
-	 */
-	format_fractional_part_nsecs(ptr, buf_remaining, (guint32)ns->nsecs, decimal_point, precision);
 }
 
 /*

programming_language: C
filename: to_str.h
change_type: ModificationType.MODIFY
diff: @@ -299,7 +299,7 @@ WS_DLL_PUBLIC gchar *ipxnet_to_str_punct(wmem_allocator_t *scope, const guint32
 
 WS_DLL_PUBLIC gchar *eui64_to_str(wmem_allocator_t *scope, const guint64 ad);
 
-WS_DLL_PUBLIC void format_fractional_part_nsecs(gchar *, size_t, guint32, const char *, int);
+WS_DLL_PUBLIC int format_fractional_part_nsecs(gchar *, size_t, guint32, const char *, int);
 
 WS_DLL_PUBLIC void display_epoch_time(gchar *, size_t, const nstime_t *, int);
 

programming_language: C