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;

Reply via email to