Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 38e2996

Browse files
committed
Issue #6608: time.asctime is now checking struct tm fields its input
before passing it to the system asctime. Patch by MunSic Jeong.
1 parent 0b0ebb4 commit 38e2996

4 files changed

Lines changed: 104 additions & 80 deletions

File tree

Lib/test/test_time.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,57 +37,60 @@ def test_strftime(self):
3737
except ValueError:
3838
self.fail('conversion specifier: %r failed.' % format)
3939

40-
def test_strftime_bounds_checking(self):
40+
def _bounds_checking(self, func=time.strftime):
4141
# Make sure that strftime() checks the bounds of the various parts
4242
#of the time tuple (0 is valid for *all* values).
4343

4444
# Check year [1900, max(int)]
45-
self.assertRaises(ValueError, time.strftime, '',
45+
self.assertRaises(ValueError, func,
4646
(1899, 1, 1, 0, 0, 0, 0, 1, -1))
4747
if time.accept2dyear:
48-
self.assertRaises(ValueError, time.strftime, '',
48+
self.assertRaises(ValueError, func,
4949
(-1, 1, 1, 0, 0, 0, 0, 1, -1))
50-
self.assertRaises(ValueError, time.strftime, '',
50+
self.assertRaises(ValueError, func,
5151
(100, 1, 1, 0, 0, 0, 0, 1, -1))
5252
# Check month [1, 12] + zero support
53-
self.assertRaises(ValueError, time.strftime, '',
53+
self.assertRaises(ValueError, func,
5454
(1900, -1, 1, 0, 0, 0, 0, 1, -1))
55-
self.assertRaises(ValueError, time.strftime, '',
55+
self.assertRaises(ValueError, func,
5656
(1900, 13, 1, 0, 0, 0, 0, 1, -1))
5757
# Check day of month [1, 31] + zero support
58-
self.assertRaises(ValueError, time.strftime, '',
58+
self.assertRaises(ValueError, func,
5959
(1900, 1, -1, 0, 0, 0, 0, 1, -1))
60-
self.assertRaises(ValueError, time.strftime, '',
60+
self.assertRaises(ValueError, func,
6161
(1900, 1, 32, 0, 0, 0, 0, 1, -1))
6262
# Check hour [0, 23]
63-
self.assertRaises(ValueError, time.strftime, '',
63+
self.assertRaises(ValueError, func,
6464
(1900, 1, 1, -1, 0, 0, 0, 1, -1))
65-
self.assertRaises(ValueError, time.strftime, '',
65+
self.assertRaises(ValueError, func,
6666
(1900, 1, 1, 24, 0, 0, 0, 1, -1))
6767
# Check minute [0, 59]
68-
self.assertRaises(ValueError, time.strftime, '',
68+
self.assertRaises(ValueError, func,
6969
(1900, 1, 1, 0, -1, 0, 0, 1, -1))
70-
self.assertRaises(ValueError, time.strftime, '',
70+
self.assertRaises(ValueError, func,
7171
(1900, 1, 1, 0, 60, 0, 0, 1, -1))
7272
# Check second [0, 61]
73-
self.assertRaises(ValueError, time.strftime, '',
73+
self.assertRaises(ValueError, func,
7474
(1900, 1, 1, 0, 0, -1, 0, 1, -1))
7575
# C99 only requires allowing for one leap second, but Python's docs say
7676
# allow two leap seconds (0..61)
77-
self.assertRaises(ValueError, time.strftime, '',
77+
self.assertRaises(ValueError, func,
7878
(1900, 1, 1, 0, 0, 62, 0, 1, -1))
7979
# No check for upper-bound day of week;
8080
# value forced into range by a ``% 7`` calculation.
8181
# Start check at -2 since gettmarg() increments value before taking
8282
# modulo.
83-
self.assertRaises(ValueError, time.strftime, '',
83+
self.assertRaises(ValueError, func,
8484
(1900, 1, 1, 0, 0, 0, -2, 1, -1))
8585
# Check day of the year [1, 366] + zero support
86-
self.assertRaises(ValueError, time.strftime, '',
86+
self.assertRaises(ValueError, func,
8787
(1900, 1, 1, 0, 0, 0, 0, -1, -1))
88-
self.assertRaises(ValueError, time.strftime, '',
88+
self.assertRaises(ValueError, func,
8989
(1900, 1, 1, 0, 0, 0, 0, 367, -1))
9090

91+
def test_strftime_bounding_check(self):
92+
self._bounds_checking(lambda tup: time.strftime('', tup))
93+
9194
def test_default_values_for_zero(self):
9295
# Make sure that using all zeros uses the proper default values.
9396
# No test for daylight savings since strftime() does not change output
@@ -120,6 +123,9 @@ def test_asctime(self):
120123
time.asctime(time.gmtime(self.t))
121124
self.assertRaises(TypeError, time.asctime, 0)
122125

126+
def test_asctime_bounding_check(self):
127+
self._bounds_checking(time.asctime)
128+
123129
def test_tzset(self):
124130
if not hasattr(time, "tzset"):
125131
return # Can't test this; don't want the test suite to fail

Misc/ACKS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ Jack Jansen
405405
Bill Janssen
406406
Drew Jenkins
407407
Flemming Kjær Jensen
408-
Jiba
408+
MunSic Jeong
409409
Orjan Johansen
410410
Fredrik Johansson
411411
Gregory K. Johnson
@@ -462,6 +462,7 @@ Ivan Krstić
462462
Andrew Kuchling
463463
Vladimir Kushnir
464464
Cameron Laird
465+
Jean-Baptiste "Jiba" Lamy
465466
Torsten Landschoff
466467
Łukasz Langa
467468
Tino Lange

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ Core and Builtins
410410
Extensions
411411
----------
412412

413+
- Issue #6608: time.asctime is now checking struct tm fields its input
414+
before passing it to the system asctime. Patch by MunSic Jeong.
415+
413416
- Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file
414417
descriptor is provided. Patch by Pascal Chambon.
415418

Modules/timemodule.c

Lines changed: 76 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ PyDoc_STRVAR(localtime_doc,
315315
Convert seconds since the Epoch to a time tuple expressing local time.\n\
316316
When 'seconds' is not passed in, convert the current time instead.");
317317

318+
/* Convert 9-item tuple to tm structure. Return 1 on success, set
319+
* an exception and return 0 on error.
320+
*/
318321
static int
319322
gettmarg(PyObject *args, struct tm *p)
320323
{
@@ -377,6 +380,76 @@ gettmarg(PyObject *args, struct tm *p)
377380
return 1;
378381
}
379382

383+
/* Check values of the struct tm fields before it is passed to strftime() and
384+
* asctime(). Return 1 if all values are valid, otherwise set an exception
385+
* and returns 0.
386+
*/
387+
static int
388+
checktm(struct tm* buf)
389+
{
390+
/* Checks added to make sure strftime() and asctime() does not crash Python by
391+
indexing blindly into some array for a textual representation
392+
by some bad index (fixes bug #897625 and #6608).
393+
394+
Also support values of zero from Python code for arguments in which
395+
that is out of range by forcing that value to the lowest value that
396+
is valid (fixed bug #1520914).
397+
398+
Valid ranges based on what is allowed in struct tm:
399+
400+
- tm_year: [0, max(int)] (1)
401+
- tm_mon: [0, 11] (2)
402+
- tm_mday: [1, 31]
403+
- tm_hour: [0, 23]
404+
- tm_min: [0, 59]
405+
- tm_sec: [0, 60]
406+
- tm_wday: [0, 6] (1)
407+
- tm_yday: [0, 365] (2)
408+
- tm_isdst: [-max(int), max(int)]
409+
410+
(1) gettmarg() handles bounds-checking.
411+
(2) Python's acceptable range is one greater than the range in C,
412+
thus need to check against automatic decrement by gettmarg().
413+
*/
414+
if (buf->tm_mon == -1)
415+
buf->tm_mon = 0;
416+
else if (buf->tm_mon < 0 || buf->tm_mon > 11) {
417+
PyErr_SetString(PyExc_ValueError, "month out of range");
418+
return 0;
419+
}
420+
if (buf->tm_mday == 0)
421+
buf->tm_mday = 1;
422+
else if (buf->tm_mday < 0 || buf->tm_mday > 31) {
423+
PyErr_SetString(PyExc_ValueError, "day of month out of range");
424+
return 0;
425+
}
426+
if (buf->tm_hour < 0 || buf->tm_hour > 23) {
427+
PyErr_SetString(PyExc_ValueError, "hour out of range");
428+
return 0;
429+
}
430+
if (buf->tm_min < 0 || buf->tm_min > 59) {
431+
PyErr_SetString(PyExc_ValueError, "minute out of range");
432+
return 0;
433+
}
434+
if (buf->tm_sec < 0 || buf->tm_sec > 61) {
435+
PyErr_SetString(PyExc_ValueError, "seconds out of range");
436+
return 0;
437+
}
438+
/* tm_wday does not need checking of its upper-bound since taking
439+
``% 7`` in gettmarg() automatically restricts the range. */
440+
if (buf->tm_wday < 0) {
441+
PyErr_SetString(PyExc_ValueError, "day of week out of range");
442+
return 0;
443+
}
444+
if (buf->tm_yday == -1)
445+
buf->tm_yday = 0;
446+
else if (buf->tm_yday < 0 || buf->tm_yday > 365) {
447+
PyErr_SetString(PyExc_ValueError, "day of year out of range");
448+
return 0;
449+
}
450+
return 1;
451+
}
452+
380453
#ifdef HAVE_STRFTIME
381454
#ifdef HAVE_WCSFTIME
382455
#define time_char wchar_t
@@ -415,69 +488,10 @@ time_strftime(PyObject *self, PyObject *args)
415488
if (tup == NULL) {
416489
time_t tt = time(NULL);
417490
buf = *localtime(&tt);
418-
} else if (!gettmarg(tup, &buf))
419-
return NULL;
420-
421-
/* Checks added to make sure strftime() does not crash Python by
422-
indexing blindly into some array for a textual representation
423-
by some bad index (fixes bug #897625).
424-
425-
Also support values of zero from Python code for arguments in which
426-
that is out of range by forcing that value to the lowest value that
427-
is valid (fixed bug #1520914).
428-
429-
Valid ranges based on what is allowed in struct tm:
430-
431-
- tm_year: [0, max(int)] (1)
432-
- tm_mon: [0, 11] (2)
433-
- tm_mday: [1, 31]
434-
- tm_hour: [0, 23]
435-
- tm_min: [0, 59]
436-
- tm_sec: [0, 60]
437-
- tm_wday: [0, 6] (1)
438-
- tm_yday: [0, 365] (2)
439-
- tm_isdst: [-max(int), max(int)]
440-
441-
(1) gettmarg() handles bounds-checking.
442-
(2) Python's acceptable range is one greater than the range in C,
443-
thus need to check against automatic decrement by gettmarg().
444-
*/
445-
if (buf.tm_mon == -1)
446-
buf.tm_mon = 0;
447-
else if (buf.tm_mon < 0 || buf.tm_mon > 11) {
448-
PyErr_SetString(PyExc_ValueError, "month out of range");
449-
return NULL;
450-
}
451-
if (buf.tm_mday == 0)
452-
buf.tm_mday = 1;
453-
else if (buf.tm_mday < 0 || buf.tm_mday > 31) {
454-
PyErr_SetString(PyExc_ValueError, "day of month out of range");
455-
return NULL;
456-
}
457-
if (buf.tm_hour < 0 || buf.tm_hour > 23) {
458-
PyErr_SetString(PyExc_ValueError, "hour out of range");
459-
return NULL;
460-
}
461-
if (buf.tm_min < 0 || buf.tm_min > 59) {
462-
PyErr_SetString(PyExc_ValueError, "minute out of range");
463-
return NULL;
464491
}
465-
if (buf.tm_sec < 0 || buf.tm_sec > 61) {
466-
PyErr_SetString(PyExc_ValueError, "seconds out of range");
492+
else if (!gettmarg(tup, &buf) || !checktm(&buf))
467493
return NULL;
468-
}
469-
/* tm_wday does not need checking of its upper-bound since taking
470-
``% 7`` in gettmarg() automatically restricts the range. */
471-
if (buf.tm_wday < 0) {
472-
PyErr_SetString(PyExc_ValueError, "day of week out of range");
473-
return NULL;
474-
}
475-
if (buf.tm_yday == -1)
476-
buf.tm_yday = 0;
477-
else if (buf.tm_yday < 0 || buf.tm_yday > 365) {
478-
PyErr_SetString(PyExc_ValueError, "day of year out of range");
479-
return NULL;
480-
}
494+
481495
/* Normalize tm_isdst just in case someone foolishly implements %Z
482496
based on the assumption that tm_isdst falls within the range of
483497
[-1, 1] */
@@ -603,7 +617,7 @@ time_asctime(PyObject *self, PyObject *args)
603617
if (tup == NULL) {
604618
time_t tt = time(NULL);
605619
buf = *localtime(&tt);
606-
} else if (!gettmarg(tup, &buf))
620+
} else if (!gettmarg(tup, &buf) || !checktm(&buf))
607621
return NULL;
608622
p = asctime(&buf);
609623
if (p[24] == '\n')

0 commit comments

Comments
 (0)