From: Bruno Haible Date: Fri, 14 Jun 2024 17:53:42 +0000 (+0200) Subject: vasnprintf: Reject a width > INT_MAX. X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=2dd75cb9bffddc83c4d4e763c98e1651ed9638dd;p=gnulib.git vasnprintf: Reject a width > INT_MAX. * lib/vasnprintf.c (VASNPRINTF): If a width is > INT_MAX, fail with EOVERFLOW. * tests/test-vasnprintf.c: Include . (test_function): Test huge widths. * tests/test-vasnwprintf.c: Include . (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 . * tests/unistdio/test-ulc-vasnprintf1.c: Likewise. --- diff --git a/ChangeLog b/ChangeLog index 76d15ccf4f..f44aab79f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2024-06-14 Bruno Haible + + vasnprintf: Reject a width > INT_MAX. + * lib/vasnprintf.c (VASNPRINTF): If a width is > INT_MAX, fail with + EOVERFLOW. + * tests/test-vasnprintf.c: Include . + (test_function): Test huge widths. + * tests/test-vasnwprintf.c: Include . + (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 . + * tests/unistdio/test-ulc-vasnprintf1.c: Likewise. + 2024-06-14 Bruno Haible relocatable-prog: Implement --help and --version for all programs. diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 03a7da71f9..0242dd3f55 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -85,7 +85,7 @@ #include /* memcpy(), strlen() */ #include /* mbstate_t, mbrtowc(), mbrlen(), wcrtomb(), mbszero() */ #include /* errno */ -#include /* CHAR_BIT, INT_WIDTH, LONG_WIDTH */ +#include /* CHAR_BIT, INT_MAX, INT_WIDTH, LONG_WIDTH */ #include /* DBL_MAX_EXP, LDBL_MAX_EXP, LDBL_MANT_DIG */ #if HAVE_NL_LANGINFO # include @@ -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; diff --git a/tests/test-vasnprintf.c b/tests/test-vasnprintf.c index 655603f7e1..d6182dc461 100644 --- a/tests/test-vasnprintf.c +++ b/tests/test-vasnprintf.c @@ -20,6 +20,7 @@ #include "vasnprintf.h" +#include #include #include #include @@ -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 * diff --git a/tests/test-vasnwprintf.c b/tests/test-vasnwprintf.c index 5d6034045e..a5832a9080 100644 --- a/tests/test-vasnwprintf.c +++ b/tests/test-vasnwprintf.c @@ -20,6 +20,7 @@ #include "vasnwprintf.h" +#include #include #include #include @@ -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 * diff --git a/tests/unistdio/test-u16-asnprintf1.h b/tests/unistdio/test-u16-asnprintf1.h index 4ec31a431c..2b1a9ab317 100644 --- a/tests/unistdio/test-u16-asnprintf1.h +++ b/tests/unistdio/test-u16-asnprintf1.h @@ -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); + } } diff --git a/tests/unistdio/test-u32-asnprintf1.h b/tests/unistdio/test-u32-asnprintf1.h index 85fa2cf078..af78684891 100644 --- a/tests/unistdio/test-u32-asnprintf1.h +++ b/tests/unistdio/test-u32-asnprintf1.h @@ -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); + } } diff --git a/tests/unistdio/test-u8-asnprintf1.h b/tests/unistdio/test-u8-asnprintf1.h index f48e2365cb..7c2c3622f4 100644 --- a/tests/unistdio/test-u8-asnprintf1.h +++ b/tests/unistdio/test-u8-asnprintf1.h @@ -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); + } } diff --git a/tests/unistdio/test-ulc-asnprintf1.c b/tests/unistdio/test-ulc-asnprintf1.c index ced342e365..8fb91da026 100644 --- a/tests/unistdio/test-ulc-asnprintf1.c +++ b/tests/unistdio/test-ulc-asnprintf1.c @@ -20,6 +20,7 @@ #include "unistdio.h" +#include #include #include #include diff --git a/tests/unistdio/test-ulc-asnprintf1.h b/tests/unistdio/test-ulc-asnprintf1.h index 9e11f31465..8ede282f55 100644 --- a/tests/unistdio/test-ulc-asnprintf1.h +++ b/tests/unistdio/test-ulc-asnprintf1.h @@ -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); + } } diff --git a/tests/unistdio/test-ulc-vasnprintf1.c b/tests/unistdio/test-ulc-vasnprintf1.c index 83557dbb4c..4352ff038b 100644 --- a/tests/unistdio/test-ulc-vasnprintf1.c +++ b/tests/unistdio/test-ulc-vasnprintf1.c @@ -20,6 +20,7 @@ #include "unistdio.h" +#include #include #include #include