summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_datetime.py20
-rw-r--r--Modules/datetimemodule.c12
2 files changed, 30 insertions, 2 deletions
diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
index c6dbb48..347b1a9 100644
--- a/Lib/test/test_datetime.py
+++ b/Lib/test/test_datetime.py
@@ -1029,6 +1029,26 @@ class TestDate(HarmlessMixedComparison):
self.assertEqual(dt1.toordinal(), dt2.toordinal())
self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
+ def test_backdoor_resistance(self):
+ # For fast unpickling, the constructor accepts a pickle string.
+ # This is a low-overhead backdoor. A user can (by intent or
+ # mistake) pass a string directly, which (if it's the right length)
+ # will get treated like a pickle, and bypass the normal sanity
+ # checks in the constructor. This can create insane objects.
+ # The constructor doesn't want to burn the time to validate all
+ # fields, but does check the month field. This stops, e.g.,
+ # datetime.datetime('1995-03-25') from yielding an insane object.
+ base = '1995-03-25'
+ if not issubclass(self.theclass, datetime):
+ base = base[:4]
+ for month_byte in '9', chr(0), chr(13), '\xff':
+ self.assertRaises(TypeError, self.theclass,
+ base[:2] + month_byte + base[3:])
+ for ord_byte in range(1, 13):
+ # This shouldn't blow up because of the month byte alone. If
+ # the implementation changes to do more-careful checking, it may
+ # blow up because other fields are insane.
+ self.theclass(base[:2] + chr(ord_byte) + base[3:])
#############################################################################
# datetime tests
diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c
index c68c368..225a6b1 100644
--- a/Modules/datetimemodule.c
+++ b/Modules/datetimemodule.c
@@ -80,6 +80,12 @@
*/
#define HASTZINFO(p) (((_PyDateTime_BaseTZInfo *)(p))->hastzinfo)
+/* M is a char or int claiming to be a valid month. The macro is equivalent
+ * to the two-sided Python test
+ * 1 <= M <= 12
+ */
+#define MONTH_IS_SANE(M) ((unsigned int)(M) - 1 < 12)
+
/* Forward declarations. */
static PyTypeObject PyDateTime_DateType;
static PyTypeObject PyDateTime_DateTimeType;
@@ -2195,7 +2201,8 @@ date_new(PyTypeObject *type, PyObject *args, PyObject *kw)
/* Check for invocation from pickle with __getstate__ state */
if (PyTuple_GET_SIZE(args) == 1 &&
PyString_Check(state = PyTuple_GET_ITEM(args, 0)) &&
- PyString_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE)
+ PyString_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE &&
+ MONTH_IS_SANE(PyString_AS_STRING(state)[2]))
{
PyDateTime_Date *me;
@@ -3550,7 +3557,8 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
if (PyTuple_GET_SIZE(args) >= 1 &&
PyTuple_GET_SIZE(args) <= 2 &&
PyString_Check(state = PyTuple_GET_ITEM(args, 0)) &&
- PyString_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE)
+ PyString_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE &&
+ MONTH_IS_SANE(PyString_AS_STRING(state)[2]))
{
PyDateTime_DateTime *me;
char aware;