+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.
#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>
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
}
{
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
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. */
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
}
has_precision = 0;
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
}
has_precision = 0;
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
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))
not have this limitation. */
return result;
-#if USE_SNPRINTF
overflow:
errno = EOVERFLOW;
goto fail_with_errno;
-#endif
out_of_memory:
errno = ENOMEM;
#include "vasnprintf.h"
+#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
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 *
#include "vasnwprintf.h"
+#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
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 *
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);
+ }
}
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);
+ }
}
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);
+ }
}
#include "unistdio.h"
+#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
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);
+ }
}
#include "unistdio.h"
+#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>