Re: [PATCH] vasnprintf: fix ("%*d", INT_MAX, n)
Bruno Haible via Gnulib discussion list <[email protected]> Mon, 13 Jul 2026 15:34:37 +0200
| Newsgroups | gmane.comp.lib.gnulib.bugs |
|---|---|
| Message-ID | <3519487.NgBsaNRSFp@cagnes> |
Paul Eggert wrote:
> The problem is that ("%*d", INT_MAX, n) fails because the code
> refuses to create a buffer of size INT_MAX + 1u,
> due to problems when dealing with older nonconforming snprintf.
Thanks for working on this.
The change introduces a compilation error on MSVC:
D:\a\ci-testdir-check\ci-testdir-check\testdir-all\gllib\vasnprintf.c(231): fatal error C1017: invalid integer constant expression
The cause is that you assumed that the various platform-specific
macros expand to non-zero numbers. Which is not the case.
This patch fixes it. Also, for maintainability, don't declare multiple
variables in a single declaration.
2026-07-13 Bruno Haible <[email protected]>
vasnprintf: Fix compilation error on MSVC (regression 2026-07-06).
* lib/vasnprintf.c (USE_SNPRINTF): Fix condition.
(VASNPRINTF): Avoid declaring multiple variables in one declaration.
diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c
index 98d8e4a757..60d3c8bda1 100644
--- a/lib/vasnprintf.c
+++ b/lib/vasnprintf.c
@@ -219,16 +219,19 @@
/* TCHAR_T is char. */
/* Use snprintf if it exists under the name 'snprintf' or '_snprintf'.
But don't use it if it has problems. For example,
- Solaris, QNX and z/OS sprintf fail if size == INT_MAX + 1u,
- BeOS produces no output if 0x3000000 <= size,
- and Linux libc5 with size = 1 writes without bounds, like sprintf.
+ Solaris, QNX and z/OS snprintf fail if size == INT_MAX + 1u,
+ BeOS snprintf produces no output if size >= 0x3000000,
+ and Linux libc5 with size == 1 writes without bounds, like sprintf.
BSD snprintf, which fails if size == INT_MAX + 2u, is OK for us.
Use snprintf only on known-safe platforms:
- glibc 2, Android, musl, the BSDs, macOS, Microsoft UCRT. */
+ glibc 2, musl libc, macOS, FreeBSD, NetBSD, OpenBSD, Android,
+ Microsoft UCRT. */
# if ((HAVE_SNPRINTF || HAVE_DECL__SNPRINTF) \
- && (2 <= __GLIBC__ || __ANDROID__ || MUSL_LIBC \
- || __FreeBSD__ || __DragonFly__ || __NetBSD__ || __OpenBSD__ \
- || (__APPLE__ && __MACH__) || _UCRT))
+ && (2 <= __GLIBC__ || MUSL_LIBC \
+ || (defined __APPLE__ && defined __MACH__) \
+ || (defined __FreeBSD__ || defined __DragonFly__) \
+ || defined __NetBSD__ || defined __OpenBSD__ \
+ || defined __ANDROID__ || defined _UCRT))
# define USE_SNPRINTF 1
# else
# define USE_SNPRINTF 0
@@ -6868,17 +6871,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* Keep size (in bytes) in ptrdiff_t and size_t range.
Also, generate at most INT_MAX + 1 characters
- counting the trailing null, as that is the
+ counting the trailing NUL, as that is the
maximum the API allows. */
- size_t
- bytes_max = MIN (PTRDIFF_MAX, SIZE_MAX),
- tchars_max = bytes_max / sizeof (TCHAR_T),
- API_max = MIN (tchars_max - 1, INT_MAX),
- maxlen_max = (API_max + 1
- - (API_max + 1) % TCHARS_PER_DCHAR),
- maxlen = (MIN (allocated - length,
- maxlen_max / TCHARS_PER_DCHAR)
- * TCHARS_PER_DCHAR);
+ size_t bytes_max = MIN (PTRDIFF_MAX, SIZE_MAX);
+ size_t tchars_max = bytes_max / sizeof (TCHAR_T);
+ size_t API_max = MIN (tchars_max - 1, INT_MAX);
+ size_t maxlen_max =
+ (API_max + 1) - (API_max + 1) % TCHARS_PER_DCHAR;
+ size_t maxlen =
+ MIN (allocated - length, maxlen_max / TCHARS_PER_DCHAR)
+ * TCHARS_PER_DCHAR;
# define SNPRINTF_BUF(arg) \
switch (prefix_count) \
{ \