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