mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #6608: time.asctime is now checking struct tm fields its input
before passing it to the system asctime. Patch by MunSic Jeong.
This commit is contained in:
parent
0b0ebb4837
commit
38e2996152
4 changed files with 104 additions and 80 deletions
|
@ -37,57 +37,60 @@ class TimeTestCase(unittest.TestCase):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail('conversion specifier: %r failed.' % format)
|
self.fail('conversion specifier: %r failed.' % format)
|
||||||
|
|
||||||
def test_strftime_bounds_checking(self):
|
def _bounds_checking(self, func=time.strftime):
|
||||||
# Make sure that strftime() checks the bounds of the various parts
|
# Make sure that strftime() checks the bounds of the various parts
|
||||||
#of the time tuple (0 is valid for *all* values).
|
#of the time tuple (0 is valid for *all* values).
|
||||||
|
|
||||||
# Check year [1900, max(int)]
|
# Check year [1900, max(int)]
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1899, 1, 1, 0, 0, 0, 0, 1, -1))
|
(1899, 1, 1, 0, 0, 0, 0, 1, -1))
|
||||||
if time.accept2dyear:
|
if time.accept2dyear:
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(-1, 1, 1, 0, 0, 0, 0, 1, -1))
|
(-1, 1, 1, 0, 0, 0, 0, 1, -1))
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(100, 1, 1, 0, 0, 0, 0, 1, -1))
|
(100, 1, 1, 0, 0, 0, 0, 1, -1))
|
||||||
# Check month [1, 12] + zero support
|
# Check month [1, 12] + zero support
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, -1, 1, 0, 0, 0, 0, 1, -1))
|
(1900, -1, 1, 0, 0, 0, 0, 1, -1))
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 13, 1, 0, 0, 0, 0, 1, -1))
|
(1900, 13, 1, 0, 0, 0, 0, 1, -1))
|
||||||
# Check day of month [1, 31] + zero support
|
# Check day of month [1, 31] + zero support
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, -1, 0, 0, 0, 0, 1, -1))
|
(1900, 1, -1, 0, 0, 0, 0, 1, -1))
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 32, 0, 0, 0, 0, 1, -1))
|
(1900, 1, 32, 0, 0, 0, 0, 1, -1))
|
||||||
# Check hour [0, 23]
|
# Check hour [0, 23]
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, -1, 0, 0, 0, 1, -1))
|
(1900, 1, 1, -1, 0, 0, 0, 1, -1))
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 24, 0, 0, 0, 1, -1))
|
(1900, 1, 1, 24, 0, 0, 0, 1, -1))
|
||||||
# Check minute [0, 59]
|
# Check minute [0, 59]
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 0, -1, 0, 0, 1, -1))
|
(1900, 1, 1, 0, -1, 0, 0, 1, -1))
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 0, 60, 0, 0, 1, -1))
|
(1900, 1, 1, 0, 60, 0, 0, 1, -1))
|
||||||
# Check second [0, 61]
|
# Check second [0, 61]
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 0, 0, -1, 0, 1, -1))
|
(1900, 1, 1, 0, 0, -1, 0, 1, -1))
|
||||||
# C99 only requires allowing for one leap second, but Python's docs say
|
# C99 only requires allowing for one leap second, but Python's docs say
|
||||||
# allow two leap seconds (0..61)
|
# allow two leap seconds (0..61)
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 0, 0, 62, 0, 1, -1))
|
(1900, 1, 1, 0, 0, 62, 0, 1, -1))
|
||||||
# No check for upper-bound day of week;
|
# No check for upper-bound day of week;
|
||||||
# value forced into range by a ``% 7`` calculation.
|
# value forced into range by a ``% 7`` calculation.
|
||||||
# Start check at -2 since gettmarg() increments value before taking
|
# Start check at -2 since gettmarg() increments value before taking
|
||||||
# modulo.
|
# modulo.
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 0, 0, 0, -2, 1, -1))
|
(1900, 1, 1, 0, 0, 0, -2, 1, -1))
|
||||||
# Check day of the year [1, 366] + zero support
|
# Check day of the year [1, 366] + zero support
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 0, 0, 0, 0, -1, -1))
|
(1900, 1, 1, 0, 0, 0, 0, -1, -1))
|
||||||
self.assertRaises(ValueError, time.strftime, '',
|
self.assertRaises(ValueError, func,
|
||||||
(1900, 1, 1, 0, 0, 0, 0, 367, -1))
|
(1900, 1, 1, 0, 0, 0, 0, 367, -1))
|
||||||
|
|
||||||
|
def test_strftime_bounding_check(self):
|
||||||
|
self._bounds_checking(lambda tup: time.strftime('', tup))
|
||||||
|
|
||||||
def test_default_values_for_zero(self):
|
def test_default_values_for_zero(self):
|
||||||
# Make sure that using all zeros uses the proper default values.
|
# Make sure that using all zeros uses the proper default values.
|
||||||
# No test for daylight savings since strftime() does not change output
|
# No test for daylight savings since strftime() does not change output
|
||||||
|
@ -120,6 +123,9 @@ class TimeTestCase(unittest.TestCase):
|
||||||
time.asctime(time.gmtime(self.t))
|
time.asctime(time.gmtime(self.t))
|
||||||
self.assertRaises(TypeError, time.asctime, 0)
|
self.assertRaises(TypeError, time.asctime, 0)
|
||||||
|
|
||||||
|
def test_asctime_bounding_check(self):
|
||||||
|
self._bounds_checking(time.asctime)
|
||||||
|
|
||||||
def test_tzset(self):
|
def test_tzset(self):
|
||||||
if not hasattr(time, "tzset"):
|
if not hasattr(time, "tzset"):
|
||||||
return # Can't test this; don't want the test suite to fail
|
return # Can't test this; don't want the test suite to fail
|
||||||
|
|
|
@ -405,7 +405,7 @@ Jack Jansen
|
||||||
Bill Janssen
|
Bill Janssen
|
||||||
Drew Jenkins
|
Drew Jenkins
|
||||||
Flemming Kjær Jensen
|
Flemming Kjær Jensen
|
||||||
Jiba
|
MunSic Jeong
|
||||||
Orjan Johansen
|
Orjan Johansen
|
||||||
Fredrik Johansson
|
Fredrik Johansson
|
||||||
Gregory K. Johnson
|
Gregory K. Johnson
|
||||||
|
@ -462,6 +462,7 @@ Ivan Krstić
|
||||||
Andrew Kuchling
|
Andrew Kuchling
|
||||||
Vladimir Kushnir
|
Vladimir Kushnir
|
||||||
Cameron Laird
|
Cameron Laird
|
||||||
|
Jean-Baptiste "Jiba" Lamy
|
||||||
Torsten Landschoff
|
Torsten Landschoff
|
||||||
Łukasz Langa
|
Łukasz Langa
|
||||||
Tino Lange
|
Tino Lange
|
||||||
|
|
|
@ -410,6 +410,9 @@ Core and Builtins
|
||||||
Extensions
|
Extensions
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
- Issue #6608: time.asctime is now checking struct tm fields its input
|
||||||
|
before passing it to the system asctime. Patch by MunSic Jeong.
|
||||||
|
|
||||||
- Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file
|
- Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file
|
||||||
descriptor is provided. Patch by Pascal Chambon.
|
descriptor is provided. Patch by Pascal Chambon.
|
||||||
|
|
||||||
|
|
|
@ -315,6 +315,9 @@ PyDoc_STRVAR(localtime_doc,
|
||||||
Convert seconds since the Epoch to a time tuple expressing local time.\n\
|
Convert seconds since the Epoch to a time tuple expressing local time.\n\
|
||||||
When 'seconds' is not passed in, convert the current time instead.");
|
When 'seconds' is not passed in, convert the current time instead.");
|
||||||
|
|
||||||
|
/* Convert 9-item tuple to tm structure. Return 1 on success, set
|
||||||
|
* an exception and return 0 on error.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
gettmarg(PyObject *args, struct tm *p)
|
gettmarg(PyObject *args, struct tm *p)
|
||||||
{
|
{
|
||||||
|
@ -377,6 +380,76 @@ gettmarg(PyObject *args, struct tm *p)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check values of the struct tm fields before it is passed to strftime() and
|
||||||
|
* asctime(). Return 1 if all values are valid, otherwise set an exception
|
||||||
|
* and returns 0.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
checktm(struct tm* buf)
|
||||||
|
{
|
||||||
|
/* Checks added to make sure strftime() and asctime() does not crash Python by
|
||||||
|
indexing blindly into some array for a textual representation
|
||||||
|
by some bad index (fixes bug #897625 and #6608).
|
||||||
|
|
||||||
|
Also support values of zero from Python code for arguments in which
|
||||||
|
that is out of range by forcing that value to the lowest value that
|
||||||
|
is valid (fixed bug #1520914).
|
||||||
|
|
||||||
|
Valid ranges based on what is allowed in struct tm:
|
||||||
|
|
||||||
|
- tm_year: [0, max(int)] (1)
|
||||||
|
- tm_mon: [0, 11] (2)
|
||||||
|
- tm_mday: [1, 31]
|
||||||
|
- tm_hour: [0, 23]
|
||||||
|
- tm_min: [0, 59]
|
||||||
|
- tm_sec: [0, 60]
|
||||||
|
- tm_wday: [0, 6] (1)
|
||||||
|
- tm_yday: [0, 365] (2)
|
||||||
|
- tm_isdst: [-max(int), max(int)]
|
||||||
|
|
||||||
|
(1) gettmarg() handles bounds-checking.
|
||||||
|
(2) Python's acceptable range is one greater than the range in C,
|
||||||
|
thus need to check against automatic decrement by gettmarg().
|
||||||
|
*/
|
||||||
|
if (buf->tm_mon == -1)
|
||||||
|
buf->tm_mon = 0;
|
||||||
|
else if (buf->tm_mon < 0 || buf->tm_mon > 11) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "month out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (buf->tm_mday == 0)
|
||||||
|
buf->tm_mday = 1;
|
||||||
|
else if (buf->tm_mday < 0 || buf->tm_mday > 31) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "day of month out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (buf->tm_hour < 0 || buf->tm_hour > 23) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "hour out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (buf->tm_min < 0 || buf->tm_min > 59) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "minute out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (buf->tm_sec < 0 || buf->tm_sec > 61) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "seconds out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* tm_wday does not need checking of its upper-bound since taking
|
||||||
|
``% 7`` in gettmarg() automatically restricts the range. */
|
||||||
|
if (buf->tm_wday < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "day of week out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (buf->tm_yday == -1)
|
||||||
|
buf->tm_yday = 0;
|
||||||
|
else if (buf->tm_yday < 0 || buf->tm_yday > 365) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "day of year out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_STRFTIME
|
#ifdef HAVE_STRFTIME
|
||||||
#ifdef HAVE_WCSFTIME
|
#ifdef HAVE_WCSFTIME
|
||||||
#define time_char wchar_t
|
#define time_char wchar_t
|
||||||
|
@ -415,69 +488,10 @@ time_strftime(PyObject *self, PyObject *args)
|
||||||
if (tup == NULL) {
|
if (tup == NULL) {
|
||||||
time_t tt = time(NULL);
|
time_t tt = time(NULL);
|
||||||
buf = *localtime(&tt);
|
buf = *localtime(&tt);
|
||||||
} else if (!gettmarg(tup, &buf))
|
}
|
||||||
|
else if (!gettmarg(tup, &buf) || !checktm(&buf))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Checks added to make sure strftime() does not crash Python by
|
|
||||||
indexing blindly into some array for a textual representation
|
|
||||||
by some bad index (fixes bug #897625).
|
|
||||||
|
|
||||||
Also support values of zero from Python code for arguments in which
|
|
||||||
that is out of range by forcing that value to the lowest value that
|
|
||||||
is valid (fixed bug #1520914).
|
|
||||||
|
|
||||||
Valid ranges based on what is allowed in struct tm:
|
|
||||||
|
|
||||||
- tm_year: [0, max(int)] (1)
|
|
||||||
- tm_mon: [0, 11] (2)
|
|
||||||
- tm_mday: [1, 31]
|
|
||||||
- tm_hour: [0, 23]
|
|
||||||
- tm_min: [0, 59]
|
|
||||||
- tm_sec: [0, 60]
|
|
||||||
- tm_wday: [0, 6] (1)
|
|
||||||
- tm_yday: [0, 365] (2)
|
|
||||||
- tm_isdst: [-max(int), max(int)]
|
|
||||||
|
|
||||||
(1) gettmarg() handles bounds-checking.
|
|
||||||
(2) Python's acceptable range is one greater than the range in C,
|
|
||||||
thus need to check against automatic decrement by gettmarg().
|
|
||||||
*/
|
|
||||||
if (buf.tm_mon == -1)
|
|
||||||
buf.tm_mon = 0;
|
|
||||||
else if (buf.tm_mon < 0 || buf.tm_mon > 11) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "month out of range");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (buf.tm_mday == 0)
|
|
||||||
buf.tm_mday = 1;
|
|
||||||
else if (buf.tm_mday < 0 || buf.tm_mday > 31) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "day of month out of range");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (buf.tm_hour < 0 || buf.tm_hour > 23) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "hour out of range");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (buf.tm_min < 0 || buf.tm_min > 59) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "minute out of range");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (buf.tm_sec < 0 || buf.tm_sec > 61) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "seconds out of range");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* tm_wday does not need checking of its upper-bound since taking
|
|
||||||
``% 7`` in gettmarg() automatically restricts the range. */
|
|
||||||
if (buf.tm_wday < 0) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "day of week out of range");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (buf.tm_yday == -1)
|
|
||||||
buf.tm_yday = 0;
|
|
||||||
else if (buf.tm_yday < 0 || buf.tm_yday > 365) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "day of year out of range");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* Normalize tm_isdst just in case someone foolishly implements %Z
|
/* Normalize tm_isdst just in case someone foolishly implements %Z
|
||||||
based on the assumption that tm_isdst falls within the range of
|
based on the assumption that tm_isdst falls within the range of
|
||||||
[-1, 1] */
|
[-1, 1] */
|
||||||
|
@ -603,7 +617,7 @@ time_asctime(PyObject *self, PyObject *args)
|
||||||
if (tup == NULL) {
|
if (tup == NULL) {
|
||||||
time_t tt = time(NULL);
|
time_t tt = time(NULL);
|
||||||
buf = *localtime(&tt);
|
buf = *localtime(&tt);
|
||||||
} else if (!gettmarg(tup, &buf))
|
} else if (!gettmarg(tup, &buf) || !checktm(&buf))
|
||||||
return NULL;
|
return NULL;
|
||||||
p = asctime(&buf);
|
p = asctime(&buf);
|
||||||
if (p[24] == '\n')
|
if (p[24] == '\n')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue