]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
mprintf: fix integer handling in float precision
authorDaniel Stenberg <daniel@haxx.se>
Mon, 13 Jan 2025 12:24:31 +0000 (13:24 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 13 Jan 2025 22:41:38 +0000 (23:41 +0100)
In the double output function when an extremely large width and
precision is set that reaches the libcurl maximum (325), the handling of
the precision part would do wrong which could lead to bad output.

Also: work-around for single-byte buffer snprintf overflow with mingw.

Extend test 557 to verify.

Coverity CID 1638751.

Closes #15988

lib/mprintf.c
tests/libtest/lib557.c

index 1a40583c2fb5427a5d1b93956d902feea3544b79..61e352e65cc50de9d8548a599d7f6383a31dc386 100644 (file)
@@ -678,12 +678,12 @@ static int formatf(
 
   struct outsegment output[MAX_SEGMENTS];
   struct va_input input[MAX_PARAMETERS];
-  char work[BUFFSIZE];
+  char work[BUFFSIZE + 2];
 
   /* 'workend' points to the final buffer byte position, but with an extra
      byte as margin to avoid the (FALSE?) warning Coverity gives us
      otherwise */
-  char *workend = &work[sizeof(work) - 2];
+  char *workend = &work[BUFFSIZE - 2];
 
   /* Parse the format string */
   if(parsefmt(format, output, input, &ocount, &icount, ap_save))
@@ -966,8 +966,8 @@ number:
 
       if(width >= 0) {
         size_t dlen;
-        if(width >= (int)sizeof(work))
-          width = sizeof(work)-1;
+        if(width >= BUFFSIZE)
+          width = BUFFSIZE - 1;
         /* RECURSIVE USAGE */
         dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
         fptr += dlen;
@@ -976,17 +976,19 @@ number:
       if(prec >= 0) {
         /* for each digit in the integer part, we can have one less
            precision */
-        size_t maxprec = sizeof(work) - 2;
+        int maxprec = BUFFSIZE - 1;
         double val = iptr->val.dnum;
+        if(prec > maxprec)
+          prec = maxprec - 1;
         if(width > 0 && prec <= width)
-          maxprec -= (size_t)width;
+          maxprec -= width;
         while(val >= 10.0) {
           val /= 10;
           maxprec--;
         }
 
-        if(prec > (int)maxprec)
-          prec = (int)maxprec-1;
+        if(prec > maxprec)
+          prec = maxprec - 1;
         if(prec < 0)
           prec = 0;
         /* RECURSIVE USAGE */
@@ -1012,14 +1014,19 @@ number:
       /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
          output characters */
 #ifdef HAVE_SNPRINTF
-      (snprintf)(work, sizeof(work), formatbuf, iptr->val.dnum); /* NOLINT */
+      (snprintf)(work, BUFFSIZE, formatbuf, iptr->val.dnum); /* NOLINT */
 #else
       (sprintf)(work, formatbuf, iptr->val.dnum);
 #endif
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
-      DEBUGASSERT(strlen(work) <= sizeof(work));
+      DEBUGASSERT(strlen(work) < BUFFSIZE);
+#ifdef __MINGW32__
+      /* Work-around for a nasty bug seen with old-mingw and gcc 7.3.0 when it
+         writes one byte more than permitted. */
+      work[BUFFSIZE - 1] = 0;
+#endif
       for(fptr = work; *fptr; fptr++)
         OUTCHAR(*fptr);
       break;
index d84626036f24556426ce04bef34461e889bdffe3..e0e7dfb36df7fda211f5d5dae94d52f1e2187457 100644 (file)
@@ -1207,6 +1207,46 @@ static int test_pos_arguments(void)
   return errors;
 }
 
+static int test_width_precision(void)
+{
+  /* 325 is max precision (and width) for a double */
+  char larger[1024];
+#define SPACE60 "                                                            "
+#define SPACE300 SPACE60 SPACE60 SPACE60 SPACE60 SPACE60
+#define OK325 SPACE300 "                        0"
+
+  int rc;
+  int errors = 0;
+  rc = curl_msnprintf(larger, sizeof(larger), "%325.325f", 0.1);
+  if(rc != 325)
+    errors++;
+  errors += string_check(larger, OK325);
+
+  rc = curl_msnprintf(larger, sizeof(larger), "%326.326f", 0.1);
+  if(rc != 325)
+    errors++;
+  errors += string_check(larger, OK325);
+
+  rc = curl_msnprintf(larger, sizeof(larger), "%1000.1000f", 0.1);
+  if(rc != 325)
+    errors++;
+  errors += string_check(larger, OK325);
+
+  rc = curl_msnprintf(larger, sizeof(larger), "%324.324f", 0.1);
+  if(rc != 324)
+    errors++;
+  rc = curl_msnprintf(larger, sizeof(larger), "%324.0f", 0.1);
+  if(rc != 324)
+    errors++;
+  rc = curl_msnprintf(larger, sizeof(larger), "%0.324f", 0.1);
+  if(rc != 325)
+    errors++;
+
+  return errors;
+}
+
+
+
 static int test_weird_arguments(void)
 {
   int errors = 0;
@@ -1320,6 +1360,8 @@ static int test_weird_arguments(void)
 
   errors += string_check(buf, "");
 
+  errors += test_width_precision();
+
   if(errors)
     printf("Some curl_mprintf() weird arguments tests failed!\n");