While reviewing changes to coreutils I noticed that nstrftime (NULL, SIZE_MAX, ...) + 1 could overflow, because nstrftime could return PTRDIFF_MAX. This pattern is so common that I installed the attached minor patch so that nstrftime can return at most PTRDIFF_MAX - 1.

Alternatively, we could change the size argument of nstrftime to be of type idx_t rather than size_t. This might be cleaner, because there would no longer need to be a runtime check for too-large size.

From f9b83b5c7ab7fa9f73ef74aa4c0b0f92c5f385c8 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Sat, 1 Nov 2025 18:45:12 -0600
Subject: [PATCH] nstrftime: do not return PTRDIFF_MAX

Previously, nstrftime (NULL, SIZE_MAX, ...) could return
PTRDIFF_MAX, which would cause problems in the common case
where the caller adds 1 to the result in order to allocate.
To avoid this, arrange for nstrftime to return at most PTRDIFF_MAX - 1.
* lib/strftime.c (__strftime_internal) [FAILURE && !FPRINTFTIME]:
Silently ceiling MAXSIZE to PTRDIFF_MAX.
---
 ChangeLog      | 8 ++++++++
 lib/strftime.c | 3 +++
 lib/strftime.h | 7 +++++++
 3 files changed, 18 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index b2f223fc6a..1d2195ca0f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2025-11-01  Paul Eggert  <[email protected]>
 
+	nstrftime: do not return PTRDIFF_MAX
+	Previously, nstrftime (NULL, SIZE_MAX, ...) could return
+	PTRDIFF_MAX, which would cause problems in the common case
+	where the caller adds 1 to the result in order to allocate.
+	To avoid this, arrange for nstrftime to return at most PTRDIFF_MAX - 1.
+	* lib/strftime.c (__strftime_internal) [FAILURE && !FPRINTFTIME]:
+	Silently ceiling MAXSIZE to PTRDIFF_MAX.
+
 	nstrftime: fix very-unlikely integer overflow issues
 	* lib/strftime.c (SBYTE_COUNT_MAX): Remove.
 	(incr_overflow): New macro.
diff --git a/lib/strftime.c b/lib/strftime.c
index 5ed3cf5900..6445d6e3d2 100644
--- a/lib/strftime.c
+++ b/lib/strftime.c
@@ -1189,6 +1189,9 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
 #endif
 #if FAILURE == 0
   int saved_errno = errno;
+#elif !FPRINTFTIME
+  if (PTRDIFF_MAX < maxsize)
+    maxsize = PTRDIFF_MAX;
 #endif
 
 #ifdef _NL_CURRENT
diff --git a/lib/strftime.h b/lib/strftime.h
index 52385dce0a..bb2b63b075 100644
--- a/lib/strftime.h
+++ b/lib/strftime.h
@@ -76,6 +76,13 @@ extern "C" {
    If unsuccessful, possibly change the array __S, set errno, and return -1;
    errno == ERANGE means the string didn't fit.
 
+   As a glibc extension if __S is null, do not store anything, and
+   return the value that would have been returned had __S been non-null.
+
+   A __MAXSIZE greater than PTRDIFF_MAX is silently treated as if
+   it were PTRDIFF_MAX, so that the caller can safely add 1 to
+   any return value without overflow.
+
    This function is like strftime, but with two more arguments:
      * __TZ instead of the local timezone information,
      * __NS as the number of nanoseconds in the %N directive,
-- 
2.51.0

Reply via email to