[PATCH] time: Fix integer truncation in strftime (bug 34538)

Florian Weimer <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
Extremely large time zone names were not processed correctly.

---
 time/Makefile        |  1 +
 time/strftime_l.c    | 18 +++++++-------
 time/tst-strftime5.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 79 insertions(+), 9 deletions(-)

diff --git a/time/Makefile b/time/Makefile
index b28471cc9e..be92398045 100644
--- a/time/Makefile
+++ b/time/Makefile
@@ -75,6 +75,7 @@ tests := \
   tst-strftime2 \
   tst-strftime3 \
   tst-strftime4 \
+  tst-strftime5 \
   tst-strptime \
   tst-strptime-whitespace \
   tst-strptime2 \
diff --git a/time/strftime_l.c b/time/strftime_l.c
index 12bc5794be..9f2917134a 100644
--- a/time/strftime_l.c
+++ b/time/strftime_l.c
@@ -198,7 +198,7 @@ static const CHAR_T zeroes[16] = /* "0000000000000000" */
 
 # define memset_space(P, Len) \
   do {									      \
-    int _len = (Len);							      \
+    size_t _len = (Len);						      \
 									      \
     do									      \
       {									      \
@@ -206,12 +206,12 @@ static const CHAR_T zeroes[16] = /* "0000000000000000" */
 	(P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T));		      \
 	_len -= _this;							      \
       }									      \
-    while (_len > 0);							      \
+    while (_len != 0);							      \
   } while (0)
 
 # define memset_zero(P, Len) \
   do {									      \
-    int _len = (Len);							      \
+    size_t _len = (Len);						      \
 									      \
     do									      \
       {									      \
@@ -219,7 +219,7 @@ static const CHAR_T zeroes[16] = /* "0000000000000000" */
 	(P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T));		      \
 	_len -= _this;							      \
       }									      \
-    while (_len > 0);							      \
+    while (_len != 0);							      \
   } while (0)
 #else
 # ifdef COMPILE_WIDE
@@ -234,14 +234,14 @@ static const CHAR_T zeroes[16] = /* "0000000000000000" */
 #define add(n, f)							      \
   do									      \
     {									      \
-      int _n = (n);							      \
-      int _delta = width - _n;						      \
-      int _incr = _n + (_delta > 0 ? _delta : 0);			      \
-      if ((size_t) _incr >= maxsize - i)				      \
+      size_t _n = (n);							      \
+      size_t _delta = (width < 0 || (size_t) width < _n) ? 0 : width - _n;    \
+      size_t _incr = _n + _delta;					      \
+      if (_incr >= maxsize - i)						      \
 	return 0;							      \
       if (p)								      \
 	{								      \
-	  if (_delta > 0)						      \
+	  if (_delta != 0)						      \
 	    {								      \
 	      if (pad == L_('0'))					      \
 		memset_zero (p, _delta);				      \
diff --git a/time/tst-strftime5.c b/time/tst-strftime5.c
new file mode 100644
index 0000000000..e625312731
--- /dev/null
+++ b/time/tst-strftime5.c
@@ -0,0 +1,69 @@
+/* Test strftime with a large time zone name.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <time.h>
+
+#include <libc-diag.h>
+#include <limits.h>
+#include <stddef.h>
+#include <support/blob_repeat.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  /* 6 is chosen so that the truncated value triggered the original bug.  */
+  enum { size = (size_t) INT_MAX + 6 };
+  struct support_blob_repeat repeat = support_blob_repeat_allocate
+    ("X", 1, size);
+  if (repeat.start == NULL)
+    FAIL_UNSUPPORTED ("could not allocate buffer for time zone name");
+  char *tzname = repeat.start;
+  tzname[size - 1] = '\0';
+
+  /* Time at the epoch with a fake time zone name.  */
+  struct tm tmbuf =
+    {
+      .tm_mday = 1,
+      .tm_zone = tzname,
+    };
+
+  char buf[10];
+  buf[9] = 'A';
+
+  TEST_COMPARE (strftime (buf, 9, "%Z", &tmbuf), 0);
+  TEST_COMPARE (buf[9], 'A');
+
+  /* GCC complains about using a width (3) below with %Z, but it is
+     supported in the glibc implementation.  */
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT (0, "-Wformat");
+
+  TEST_COMPARE (strftime (buf, 9, "%3Z", &tmbuf), 0);
+  TEST_COMPARE (buf[9], 'A');
+
+  TEST_COMPARE (strftime (buf, 9, "%03Z", &tmbuf), 0);
+  TEST_COMPARE (buf[9], 'A');
+
+  DIAG_POP_NEEDS_COMMENT;
+
+  support_blob_repeat_free (&repeat);
+  return 0;
+}
+
+#include <support/test-driver.c>

base-commit: a6bf80129e24bd782919a3325e672587ab6ff406
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.