Index: Lib/test/test_time.py =================================================================== --- Lib/test/test_time.py (revision 87690) +++ Lib/test/test_time.py (working copy) @@ -123,33 +123,22 @@ time.asctime(time.gmtime(self.t)) self.assertRaises(TypeError, time.asctime, 0) self.assertRaises(TypeError, time.asctime, ()) - # XXX: POSIX-compliant asctime should refuse to convert year > 9999, - # but glibc implementation does not. For now, just check it doesn't - # segfault as it did before, and the result contains no newline. - try: - result = time.asctime((12345, 1, 0, 0, 0, 0, 0, 0, 0)) - except ValueError: - # for POSIX-compliant runtimes - pass - else: - self.assertNotIn('\n', result) + self.assertRaises(ValueError, time.asctime, (12345,) + (0,) * 8) def test_asctime_bounding_check(self): self._bounds_checking(time.asctime) def test_ctime(self): - # XXX: POSIX-compliant ctime should refuse to convert year > 9999, - # but glibc implementation does not. For now, just check it doesn't - # segfault as it did before, and the result contains no newline. - try: - result = time.ctime(1e12) - except ValueError: - # for POSIX-compliant runtimes (or 32-bit systems, where time_t - # cannot hold timestamps with a five-digit year) - pass - else: - self.assertNotIn('\n', result) + self.assertRaises(ValueError, time.ctime, 1e12) + # The following constant is time.mktime((10000, 1, 10, ..)). + # We don't compute it here because mktime may fail on some + # platforms for large years. Hopefully no sane TZ will bring + # this date to a valid range. + bigval = 253403078400 + self.assertRaises(ValueError, time.ctime, bigval) + + @unittest.skipIf(not hasattr(time, "tzset"), "time module has no attribute tzset") def test_tzset(self): Index: Modules/timemodule.c =================================================================== --- Modules/timemodule.c (revision 87690) +++ Modules/timemodule.c (working copy) @@ -611,7 +611,7 @@ { PyObject *tup = NULL; struct tm buf; - char *p, *q; + char *p; if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup)) return NULL; if (tup == NULL) { @@ -619,17 +619,15 @@ buf = *localtime(&tt); } else if (!gettmarg(tup, &buf) || !checktm(&buf)) return NULL; - p = asctime(&buf); - if (p == NULL) { - PyErr_SetString(PyExc_ValueError, "unconvertible time"); + if (buf.tm_year + 1900 > 9999 || + buf.tm_year + 1900 < -999) { + PyErr_SetString(PyExc_ValueError, "year out of range"); return NULL; } - /* Replace a terminating newline by a null byte, normally at position 24. - * It can occur later if the year has more than four digits. */ - for (q = p+24; *q != '\0'; q++) - if (*q == '\n') - *q = '\0'; - return PyUnicode_FromString(p); + p = asctime(&buf); + assert(p != NULL); + assert(p[24] == '\n'); + return PyUnicode_FromStringAndSize(p, 24); } PyDoc_STRVAR(asctime_doc, @@ -644,7 +642,8 @@ { PyObject *ot = NULL; time_t tt; - char *p, *q; + char *p; + struct tm *pbuf; if (!PyArg_UnpackTuple(args, "ctime", 0, 1, &ot)) return NULL; @@ -658,17 +657,26 @@ if (tt == (time_t)-1 && PyErr_Occurred()) return NULL; } - p = ctime(&tt); - if (p == NULL) { + pbuf = localtime(&tt); + if (pbuf == NULL) { PyErr_SetString(PyExc_ValueError, "unconvertible time"); + return NULL; + } + /* XXX: localtime should not return a struct tm that is not valid input + * for asctime on a given platform, but out of abundance of caution, we + * check the fields here. + */ + if (!checktm(pbuf)) return NULL; + if (pbuf->tm_year + 1900 > 9999 || + pbuf->tm_year + 1900 < -999) { + PyErr_SetString(PyExc_ValueError, "year out of range"); + return NULL; } - /* Replace a terminating newline by a null byte, normally at position 24. - * It can occur later if the year has more than four digits. */ - for (q = p+24; *q != '\0'; q++) - if (*q == '\n') - *q = '\0'; - return PyUnicode_FromString(p); + p = asctime(pbuf); + assert(p != NULL); + assert(p[24] == '\n'); + return PyUnicode_FromStringAndSize(p, 24); } PyDoc_STRVAR(ctime_doc,