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

Florian Weimer via Glibc-cvs <[email protected]>
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=a0faa928b094be829c52d1e493442ba3b48954b1

commit a0faa928b094be829c52d1e493442ba3b48954b1
Author: Florian Weimer <[email protected]>
Date:   Wed Aug 19 09:33:31 2026 +0200

    time: Fix integer truncation in strftime (bug 34538)
    
    Extremely large time zone names were not processed correctly.

Diff:
---
 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>
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.