]> Savannah Git Hosting - gnulib.git/commitdiff
vasnprintf: Reject a width > INT_MAX.
authorBruno Haible <bruno@clisp.org>
Fri, 14 Jun 2024 17:53:42 +0000 (19:53 +0200)
committerBruno Haible <bruno@clisp.org>
Fri, 14 Jun 2024 17:53:42 +0000 (19:53 +0200)
* lib/vasnprintf.c (VASNPRINTF): If a width is > INT_MAX, fail with
EOVERFLOW.
* tests/test-vasnprintf.c: Include <errno.h>.
(test_function): Test huge widths.
* tests/test-vasnwprintf.c: Include <errno.h>.
(test_function): Test huge widths.
* tests/unistdio/test-u8-asnprintf1.h (test_function): Test huge widths.
* tests/unistdio/test-u16-asnprintf1.h (test_function): Likewise.
* tests/unistdio/test-u32-asnprintf1.h (test_function): Likewise.
* tests/unistdio/test-ulc-asnprintf1.h (test_function): Likewise.
* tests/unistdio/test-ulc-asnprintf1.c: Include <errno.h>.
* tests/unistdio/test-ulc-vasnprintf1.c: Likewise.

ChangeLog
lib/vasnprintf.c
tests/test-vasnprintf.c
tests/test-vasnwprintf.c
tests/unistdio/test-u16-asnprintf1.h
tests/unistdio/test-u32-asnprintf1.h
tests/unistdio/test-u8-asnprintf1.h
tests/unistdio/test-ulc-asnprintf1.c
tests/unistdio/test-ulc-asnprintf1.h
tests/unistdio/test-ulc-vasnprintf1.c

index 76d15ccf4fe08310cf478d0ff36a27ad99c4d486..f44aab79f6717fdfdc48029c02c6d2c878d2771b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2024-06-14  Bruno Haible  <bruno@clisp.org>
+
+       vasnprintf: Reject a width > INT_MAX.
+       * lib/vasnprintf.c (VASNPRINTF): If a width is > INT_MAX, fail with
+       EOVERFLOW.
+       * tests/test-vasnprintf.c: Include <errno.h>.
+       (test_function): Test huge widths.
+       * tests/test-vasnwprintf.c: Include <errno.h>.
+       (test_function): Test huge widths.
+       * tests/unistdio/test-u8-asnprintf1.h (test_function): Test huge widths.
+       * tests/unistdio/test-u16-asnprintf1.h (test_function): Likewise.
+       * tests/unistdio/test-u32-asnprintf1.h (test_function): Likewise.
+       * tests/unistdio/test-ulc-asnprintf1.h (test_function): Likewise.
+       * tests/unistdio/test-ulc-asnprintf1.c: Include <errno.h>.
+       * tests/unistdio/test-ulc-vasnprintf1.c: Likewise.
+
 2024-06-14  Bruno Haible  <bruno@clisp.org>
 
        relocatable-prog: Implement --help and --version for all programs.
index 03a7da71f9e5ae73a6f1c626e08a804f5c641e6e..0242dd3f55b0e85334c870509c9399053c9afa81 100644 (file)
@@ -85,7 +85,7 @@
 #include <string.h>     /* memcpy(), strlen() */
 #include <wchar.h>      /* mbstate_t, mbrtowc(), mbrlen(), wcrtomb(), mbszero() */
 #include <errno.h>      /* errno */
-#include <limits.h>     /* CHAR_BIT, INT_WIDTH, LONG_WIDTH */
+#include <limits.h>     /* CHAR_BIT, INT_MAX, INT_WIDTH, LONG_WIDTH */
 #include <float.h>      /* DBL_MAX_EXP, LDBL_MAX_EXP, LDBL_MANT_DIG */
 #if HAVE_NL_LANGINFO
 # include <langinfo.h>
@@ -2458,6 +2458,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                     has_width = 1;
                   }
 
@@ -2845,6 +2847,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                   }
 
                 {
@@ -3000,6 +3004,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                     has_width = 1;
                   }
 
@@ -3441,6 +3447,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                     has_width = 1;
                   }
 
@@ -3629,6 +3637,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                   }
 
                 /* %c in vasnwprintf.  See the specification of fwprintf.  */
@@ -3717,6 +3727,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                     has_width = 1;
                   }
 
@@ -4031,6 +4043,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                   }
 
                 has_precision = 0;
@@ -4536,6 +4550,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
                   }
 
                 has_precision = 0;
@@ -5718,6 +5734,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                           width = xsum (xtimes (width, 10), *digitp++ - '0');
                         while (digitp != dp->width_end);
                       }
+                    if (width > (size_t) INT_MAX)
+                      goto overflow;
+# define WIDTH_IS_CHECKED 1
 # if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
                     has_width = 1;
 # endif
@@ -5867,6 +5886,43 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                     if (dp->width_start != dp->width_end)
                       {
                         size_t n = dp->width_end - dp->width_start;
+#if !WIDTH_IS_CHECKED
+                        size_t width;
+                        /* Reject an out-of-range width.
+                           The underlying SNPRINTF already does this on some
+                           platforms (glibc, musl, macOS, FreeBSD, NetBSD,
+                           OpenBSD, Cygwin, Solaris, MSVC).  However, on others
+                           (AIX, mingw), it doesn't; thus this vasnprintf
+                           invocation would succeed and produce a wrong result.
+                           So, this is redundant on some platforms, but it's a
+                           quick check anyway.  */
+                        if (dp->width_arg_index != ARG_NONE)
+                          {
+                            int arg;
+
+                            if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+                              abort ();
+                            arg = a.arg[dp->width_arg_index].a.a_int;
+                            width = arg;
+                            if (arg < 0)
+                              {
+                                /* "A negative field width is taken as a '-' flag
+                                    followed by a positive field width."  */
+                                width = -width;
+                              }
+                          }
+                        else
+                          {
+                            const FCHAR_T *digitp = dp->width_start;
+
+                            width = 0;
+                            do
+                              width = xsum (xtimes (width, 10), *digitp++ - '0');
+                            while (digitp != dp->width_end);
+                          }
+                        if (width > (size_t) INT_MAX)
+                          goto overflow;
+#endif
                         /* The width specification is known to consist only
                            of standard ASCII characters.  */
                         if (sizeof (FCHAR_T) == sizeof (TCHAR_T))
@@ -6960,11 +7016,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
        not have this limitation.  */
     return result;
 
-#if USE_SNPRINTF
   overflow:
     errno = EOVERFLOW;
     goto fail_with_errno;
-#endif
 
   out_of_memory:
     errno = ENOMEM;
index 655603f7e1f2a57c039ebf9c6e2dbe0278a26cda..d6182dc461d81062cd3ec097b4942ef6e7228286 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "vasnprintf.h"
 
+#include <errno.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -86,6 +87,58 @@ test_function (char * (*my_asnprintf) (char *, size_t *, const char *, ...))
       if (result != buf)
         free (result);
     }
+
+  /* Verify that [v]asnprintf() rejects a width > 2 GiB, < 4 GiB.  */
+  {
+    size_t length;
+    char *s = my_asnprintf (NULL, &length, "x%03000000000dy\n", -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    char *s = my_asnprintf (NULL, &length, "x%03000000000cy\n", '@');
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+
+  /* Verify that [v]asnprintf() rejects a width > 4 GiB.  */
+  {
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306dy\n", /* 2^32 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306cy\n", /* 2^32 + 10 */
+                    '@');
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626dy\n", /* 2^64 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626cy\n", /* 2^64 + 10 */
+                    '@');
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
 }
 
 static char *
index 5d6034045ed54a9760ceb9cc1a23059c1c8afc5e..a5832a90807a87c88d631ea087c3b6e71c8f9f03 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "vasnwprintf.h"
 
+#include <errno.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -87,6 +88,58 @@ test_function (wchar_t * (*my_asnwprintf) (wchar_t *, size_t *, const wchar_t *,
       if (result != buf)
         free (result);
     }
+
+  /* Verify that [v]asnwprintf() rejects a width > 2 GiB, < 4 GiB.  */
+  {
+    size_t length;
+    wchar_t *s = my_asnwprintf (NULL, &length, L"x%03000000000dy\n", -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    wchar_t *s = my_asnwprintf (NULL, &length, L"x%03000000000cy\n", '@');
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+
+  /* Verify that [v]asnwprintf() rejects a width > 4 GiB.  */
+  {
+    size_t length;
+    wchar_t *s =
+      my_asnwprintf (NULL, &length,
+                     L"x%04294967306dy\n", /* 2^32 + 10 */
+                     -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    wchar_t *s =
+      my_asnwprintf (NULL, &length,
+                     L"x%04294967306cy\n", /* 2^32 + 10 */
+                     '@');
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    wchar_t *s =
+      my_asnwprintf (NULL, &length,
+                     L"x%018446744073709551626dy\n", /* 2^64 + 10 */
+                     -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    wchar_t *s =
+      my_asnwprintf (NULL, &length,
+                     L"x%018446744073709551626cy\n", /* 2^64 + 10 */
+                     '@');
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
 }
 
 static wchar_t *
index 4ec31a431c264830864a658d570e1b2f257d95ce..2b1a9ab31701d1d9a94cc2e5a768f9008f9bd3ee 100644 (file)
@@ -57,4 +57,59 @@ test_function (uint16_t * (*my_asnprintf) (uint16_t *, size_t *, const char *, .
       if (result != buf)
         free (result);
     }
+
+  /* Verify that u16_[v]asnprintf() rejects a width > 2 GiB, < 4 GiB.  */
+  {
+    size_t length;
+    uint16_t *s = my_asnprintf (NULL, &length, "x%03000000000dy\n", -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint16_t arg[] = { '@', 0 };
+    size_t length;
+    uint16_t *s = my_asnprintf (NULL, &length, "x%03000000000lUy\n", arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+
+  /* Verify that u16_[v]asnprintf() rejects a width > 4 GiB.  */
+  {
+    size_t length;
+    uint16_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306dy\n", /* 2^32 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint16_t arg[] = { '@', 0 };
+    size_t length;
+    uint16_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306lUy\n", /* 2^32 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    uint16_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626dy\n", /* 2^64 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint16_t arg[] = { '@', 0 };
+    size_t length;
+    uint16_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626lUy\n", /* 2^64 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
 }
index 85fa2cf0784f5bc745bd97f3f775309ae450656d..af78684891ad817fdd7e243eff145437bfca003b 100644 (file)
@@ -57,4 +57,59 @@ test_function (uint32_t * (*my_asnprintf) (uint32_t *, size_t *, const char *, .
       if (result != buf)
         free (result);
     }
+
+  /* Verify that u32_[v]asnprintf() rejects a width > 2 GiB, < 4 GiB.  */
+  {
+    size_t length;
+    uint32_t *s = my_asnprintf (NULL, &length, "x%03000000000dy\n", -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint32_t arg[] = { '@', 0 };
+    size_t length;
+    uint32_t *s = my_asnprintf (NULL, &length, "x%03000000000llUy\n", arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+
+  /* Verify that u32_[v]asnprintf() rejects a width > 4 GiB.  */
+  {
+    size_t length;
+    uint32_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306dy\n", /* 2^32 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint32_t arg[] = { '@', 0 };
+    size_t length;
+    uint32_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306llUy\n", /* 2^32 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    uint32_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626dy\n", /* 2^64 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint32_t arg[] = { '@', 0 };
+    size_t length;
+    uint32_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626llUy\n", /* 2^64 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
 }
index f48e2365cb1ff31ef97092cca4e4a64407934fc2..7c2c3622f497bc0cf368793a9212a623ee1117dd 100644 (file)
@@ -54,4 +54,59 @@ test_function (uint8_t * (*my_asnprintf) (uint8_t *, size_t *, const char *, ...
       if (result != buf)
         free (result);
     }
+
+  /* Verify that u8_[v]asnprintf() rejects a width > 2 GiB, < 4 GiB.  */
+  {
+    size_t length;
+    uint8_t *s = my_asnprintf (NULL, &length, "x%03000000000dy\n", -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint8_t arg[] = { '@', 0 };
+    size_t length;
+    uint8_t *s = my_asnprintf (NULL, &length, "x%03000000000Uy\n", arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+
+  /* Verify that u8_[v]asnprintf() rejects a width > 4 GiB.  */
+  {
+    size_t length;
+    uint8_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306dy\n", /* 2^32 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint8_t arg[] = { '@', 0 };
+    size_t length;
+    uint8_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306Uy\n", /* 2^32 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    uint8_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626dy\n", /* 2^64 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint8_t arg[] = { '@', 0 };
+    size_t length;
+    uint8_t *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626Uy\n", /* 2^64 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
 }
index ced342e36522173259140b6786ac33a6e14e4073..8fb91da026e0b7a22dd744578f552219be60c564 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "unistdio.h"
 
+#include <errno.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
index 9e11f314652dbd177a4af00393196fe71378422c..8ede282f5549091c8d11752093d03ee5bb74520a 100644 (file)
@@ -51,4 +51,59 @@ test_function (char * (*my_asnprintf) (char *, size_t *, const char *, ...))
       if (result != buf)
         free (result);
     }
+
+  /* Verify that ulc_[v]asnprintf() rejects a width > 2 GiB, < 4 GiB.  */
+  {
+    size_t length;
+    char *s = my_asnprintf (NULL, &length, "x%03000000000dy\n", -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint8_t arg[] = { '@', 0 };
+    size_t length;
+    char *s = my_asnprintf (NULL, &length, "x%03000000000Uy\n", arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+
+  /* Verify that ulc_[v]asnprintf() rejects a width > 4 GiB.  */
+  {
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306dy\n", /* 2^32 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint8_t arg[] = { '@', 0 };
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%04294967306Uy\n", /* 2^32 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626dy\n", /* 2^64 + 10 */
+                    -17);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
+  {
+    static const uint8_t arg[] = { '@', 0 };
+    size_t length;
+    char *s =
+      my_asnprintf (NULL, &length,
+                    "x%018446744073709551626Uy\n", /* 2^64 + 10 */
+                    arg);
+    ASSERT (s == NULL);
+    ASSERT (errno == EOVERFLOW);
+  }
 }
index 83557dbb4c3e8f369cecf41e7d357d090781d7ca..4352ff038b74888317a7b046ce772847cc29c39c 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "unistdio.h"
 
+#include <errno.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>