summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Belopolsky <alexander.belopolsky@gmail.com>2010-10-01 14:18:49 (GMT)
committerAlexander Belopolsky <alexander.belopolsky@gmail.com>2010-10-01 14:18:49 (GMT)
commit38e299615270e2a4a9b223b789924e899847f3cc (patch)
treee6a0533eb71f908a04051d3facaa8f3de8942dbf
parent0b0ebb4837617380ab50665a15c8cb3b3523b6ed (diff)
downloadcpython-38e299615270e2a4a9b223b789924e899847f3cc.zip
cpython-38e299615270e2a4a9b223b789924e899847f3cc.tar.gz
cpython-38e299615270e2a4a9b223b789924e899847f3cc.tar.bz2
Issue #6608: time.asctime is now checking struct tm fields its input
before passing it to the system asctime. Patch by MunSic Jeong.
-rw-r--r--Lib/test/test_time.py40
-rw-r--r--Misc/ACKS3
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/timemodule.c138
4 files changed, 104 insertions, 80 deletions
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index 4ce90e9..fbf07eb 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -37,57 +37,60 @@ class TimeTestCase(unittest.TestCase):
except ValueError:
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
#of the time tuple (0 is valid for *all* values).
# Check year [1900, max(int)]
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(1899, 1, 1, 0, 0, 0, 0, 1, -1))
if time.accept2dyear:
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(-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))
# Check month [1, 12] + zero support
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(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))
# 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))
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(1900, 1, 32, 0, 0, 0, 0, 1, -1))
# Check hour [0, 23]
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(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))
# Check minute [0, 59]
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(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))
# Check second [0, 61]
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(1900, 1, 1, 0, 0, -1, 0, 1, -1))
# C99 only requires allowing for one leap second, but Python's docs say
# allow two leap seconds (0..61)
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(1900, 1, 1, 0, 0, 62, 0, 1, -1))
# No check for upper-bound day of week;
# value forced into range by a ``% 7`` calculation.
# Start check at -2 since gettmarg() increments value before taking
# modulo.
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(1900, 1, 1, 0, 0, 0, -2, 1, -1))
# 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))
- self.assertRaises(ValueError, time.strftime, '',
+ self.assertRaises(ValueError, func,
(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):
# Make sure that using all zeros uses the proper default values.
# 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))
self.assertRaises(TypeError, time.asctime, 0)
+ def test_asctime_bounding_check(self):
+ self._bounds_checking(time.asctime)
+
def test_tzset(self):
if not hasattr(time, "tzset"):
return # Can't test this; don't want the test suite to fail
diff --git a/Misc/ACKS b/Misc/ACKS
index 228dfe7..97792ad 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -405,7 +405,7 @@ Jack Jansen
Bill Janssen
Drew Jenkins
Flemming Kjær Jensen
-Jiba
+MunSic Jeong
Orjan Johansen
Fredrik Johansson
Gregory K. Johnson
@@ -462,6 +462,7 @@ Ivan Krstić
Andrew Kuchling
Vladimir Kushnir
Cameron Laird
+Jean-Baptiste "Jiba" Lamy
Torsten Landschoff
Łukasz Langa
Tino Lange
diff --git a/Misc/NEWS b/Misc/NEWS
index 9144e25..a0d8275 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -410,6 +410,9 @@ Core and Builtins
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
descriptor is provided. Patch by Pascal Chambon.
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index acd3fc3..cbb05cd 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -315,6 +315,9 @@ PyDoc_STRVAR(localtime_doc,
Convert seconds since the Epoch to a time tuple expressing local time.\n\
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
gettmarg(PyObject *args, struct tm *p)
{
@@ -377,6 +380,76 @@ gettmarg(PyObject *args, struct tm *p)
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_WCSFTIME
#define time_char wchar_t
@@ -415,69 +488,10 @@ time_strftime(PyObject *self, PyObject *args)
if (tup == NULL) {
time_t tt = time(NULL);
buf = *localtime(&tt);
- } else if (!gettmarg(tup, &buf))
- 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");
+ else if (!gettmarg(tup, &buf) || !checktm(&buf))
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
based on the assumption that tm_isdst falls within the range of
[-1, 1] */
@@ -603,7 +617,7 @@ time_asctime(PyObject *self, PyObject *args)
if (tup == NULL) {
time_t tt = time(NULL);
buf = *localtime(&tt);
- } else if (!gettmarg(tup, &buf))
+ } else if (!gettmarg(tup, &buf) || !checktm(&buf))
return NULL;
p = asctime(&buf);
if (p[24] == '\n')