Skip to content

Commit e5b5895

Browse files
committed
Issue #24917: time_strftime() buffer over-read.
1 parent 714e493 commit e5b5895

File tree

3 files changed

+25
-6
lines changed

3 files changed

+25
-6
lines changed

Lib/test/test_time.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ def _bounds_checking(self, func):
174174
def test_strftime_bounding_check(self):
175175
self._bounds_checking(lambda tup: time.strftime('', tup))
176176

177+
def test_strftime_format_check(self):
178+
# Test that strftime does not crash on invalid format strings
179+
# that may trigger a buffer overread. When not triggered,
180+
# strftime may succeed or raise ValueError depending on
181+
# the platform.
182+
for x in [ '', 'A', '%A', '%AA' ]:
183+
for y in range(0x0, 0x10):
184+
for z in [ '%', 'A%', 'AA%', '%A%', 'A%A%', '%#' ]:
185+
try:
186+
time.strftime(x * y + z)
187+
except ValueError:
188+
pass
189+
177190
def test_default_values_for_zero(self):
178191
# Make sure that using all zeros uses the proper default
179192
# values. No test for daylight savings since strftime() does

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Core and Builtins
2020
Library
2121
-------
2222

23+
- Issue #24917: time_strftime() buffer over-read.
24+
2325
- Issue #24748: To resolve a compatibility problem found with py2exe and
2426
pywin32, imp.load_dynamic() once again ignores previously loaded modules
2527
to support Python modules replacing themselves with extension modules.

Modules/timemodule.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -610,25 +610,28 @@ time_strftime(PyObject *self, PyObject *args)
610610

611611
#if defined(MS_WINDOWS) && !defined(HAVE_WCSFTIME)
612612
/* check that the format string contains only valid directives */
613-
for(outbuf = strchr(fmt, '%');
613+
for (outbuf = strchr(fmt, '%');
614614
outbuf != NULL;
615615
outbuf = strchr(outbuf+2, '%'))
616616
{
617-
if (outbuf[1]=='#')
617+
if (outbuf[1] == '#')
618618
++outbuf; /* not documented by python, */
619-
if ((outbuf[1] == 'y') && buf.tm_year < 0)
620-
{
619+
if (outbuf[1] == '\0')
620+
break;
621+
if ((outbuf[1] == 'y') && buf.tm_year < 0) {
621622
PyErr_SetString(PyExc_ValueError,
622623
"format %y requires year >= 1900 on Windows");
623624
Py_DECREF(format);
624625
return NULL;
625626
}
626627
}
627628
#elif (defined(_AIX) || defined(sun)) && defined(HAVE_WCSFTIME)
628-
for(outbuf = wcschr(fmt, '%');
629+
for (outbuf = wcschr(fmt, '%');
629630
outbuf != NULL;
630631
outbuf = wcschr(outbuf+2, '%'))
631632
{
633+
if (outbuf[1] == L'\0')
634+
break;
632635
/* Issue #19634: On AIX, wcsftime("y", (1899, 1, 1, 0, 0, 0, 0, 0, 0))
633636
returns "0/" instead of "99" */
634637
if (outbuf[1] == L'y' && buf.tm_year < 0) {
@@ -659,7 +662,8 @@ time_strftime(PyObject *self, PyObject *args)
659662
#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__)
660663
err = errno;
661664
#endif
662-
if (buflen > 0 || i >= 256 * fmtlen) {
665+
if (buflen > 0 || fmtlen == 0 ||
666+
(fmtlen > 4 && i >= 256 * fmtlen)) {
663667
/* If the buffer is 256 times as long as the format,
664668
it's probably not failing for lack of room!
665669
More likely, the format yields an empty result,

0 commit comments

Comments
 (0)