[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