On 2026-07-25 16:59, Bruno Haible via Gnulib discussion list wrote:
+#if defined __FILC__
+ if (lenbuf > INT_MAX)
+ lenbuf = INT_MAX;
+#endif
This looks like at least an off-by-one bug in Fil-C, as it's necessary
to be able to pass a size equal to INT_MAX + 1u to snprintf, as the
return value does not count the trailing '\0'. Should Fil-C be listed as
buggy in doc/posix-functions/snprintf.texi,
doc/posix-functions/vsnprintf.texi, next to other platforms that
mishandle the sizes INT_MAX + 1u (Solaris, z/OS) or INT_MAX + 2u
(FreeBSD, OpenBSD, NetBSD, macOS)?
This reminds me of the vasnprintf fix I installed a few weeks ago[1], so
that vasnprintf works with a size equal to INT_MAX + 1u. Perhaps a
similar patch here would provide a bit more functionality on Fil-C?
Something like the attacheded untest patch, perhaps, which I stole from
lib/vasnprintf.c?
[1]: https://lists.gnu.org/r/bug-gnulib/2026-07/msg00009.html
diff --git a/lib/szprintf.c b/lib/szprintf.c
index 651aae801e..41c1796313 100644
--- a/lib/szprintf.c
+++ b/lib/szprintf.c
@@ -35,14 +35,13 @@ szprintf (char *str, const char *format, ...)
va_list args;
va_start (args, format);
- /* Set lenbuf = min (SIZE_MAX, - (uintptr_t) str - 1). */
- size_t lenbuf = SIZE_MAX;
- if (lenbuf >= ~ (uintptr_t) str)
- lenbuf = ~ (uintptr_t) str;
-#if defined __FILC__
- if (lenbuf > INT_MAX)
- lenbuf = INT_MAX;
-#endif
+ /* Keep size (in bytes) in ptrdiff_t and size_t range.
+ Also, generate at most INT_MAX + 1 bytes counting the trailing
+ NUL, as that is the maximum the API allows.
+ Also, do not exceed - (uintptr_t) str - 1. */
+ size_t bytes_max = MIN (PTRDIFF_MAX, SIZE_MAX);
+ size_t API_max = MIN (bytes_max - 1, INT_MAX);
+ size_t lenbuf = MIN (API_max, - (uintptr_t) str - 1);
char *output = vasnprintf (str, &lenbuf, format, args);
size_t len = lenbuf;
diff --git a/lib/unistdio/u-vsprintf.h b/lib/unistdio/u-vsprintf.h
index 6f93257c17..700b4ee685 100644
--- a/lib/unistdio/u-vsprintf.h
+++ b/lib/unistdio/u-vsprintf.h
@@ -30,19 +30,15 @@ int
VSPRINTF (DCHAR_T *buf, const FCHAR_T *format, va_list args)
{
/* Pass an infinite length. But note that *vasnprintf may fail if the buffer
- argument is larger than INT_MAX (if that fits into a 'size_t' at all).
+ argument is larger than INT_MAX + 1 (if that fits into a 'size_t' at all).
Also note that glibc's iconv fails with E2BIG when we pass a length that
is so large that buf + length wraps around, i.e.
(uintptr_t) (buf + length) < (uintptr_t) buf. */
- /* Set length = min (SIZE_MAX, INT_MAX, - (uintptr_t) buf - 1). */
- size_t length = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX);
- if (length > (~ (uintptr_t) buf) / sizeof (DCHAR_T))
- length = (~ (uintptr_t) buf) / sizeof (DCHAR_T);
-#if defined __FILC__
- if (length > INT_MAX / sizeof (DCHAR_T))
- length = INT_MAX / sizeof (DCHAR_T);
-#endif
+ size_t bytes_max = MIN (PTRDIFF_MAX, SIZE_MAX);
+ size_t dchars_max = bytes_max / sizeof (DCHAR_T);
+ size_t API_max = MIN (dchars_max - 1, INT_MAX);
+ size_t length = MIN (API_max, (~ (uintptr_t) buf) / sizeof (DCHAR_T));
DCHAR_T *result = VASNPRINTF (buf, &length, format, args);
if (result == NULL)
diff --git a/lib/vszprintf.c b/lib/vszprintf.c
index 2a65064541..6a55b5235a 100644
--- a/lib/vszprintf.c
+++ b/lib/vszprintf.c
@@ -32,14 +32,13 @@
ptrdiff_t
vszprintf (char *str, const char *format, va_list args)
{
- /* Set lenbuf = min (SIZE_MAX, - (uintptr_t) str - 1). */
- size_t lenbuf = SIZE_MAX;
- if (lenbuf >= ~ (uintptr_t) str)
- lenbuf = ~ (uintptr_t) str;
-#if defined __FILC__
- if (lenbuf > INT_MAX)
- lenbuf = INT_MAX;
-#endif
+ /* Keep size (in bytes) in ptrdiff_t and size_t range.
+ Also, generate at most INT_MAX + 1 bytes counting the trailing
+ NUL, as that is the maximum the API allows.
+ Also, do not exceed - (uintptr_t) str - 1. */
+ size_t bytes_max = MIN (PTRDIFF_MAX, SIZE_MAX);
+ size_t API_max = MIN (bytes_max - 1, INT_MAX);
+ size_t lenbuf = MIN (API_max, - (uintptr_t) str - 1);
char *output = vasnprintf (str, &lenbuf, format, args);
size_t len = lenbuf;