diff --git a/Doc/library/marshal.rst b/Doc/library/marshal.rst index 19a56d74a30..4f5b16ac869 100644 --- a/Doc/library/marshal.rst +++ b/Doc/library/marshal.rst @@ -44,12 +44,6 @@ and dictionaries are only supported as long as the values contained therein are themselves supported; and recursive lists and dictionaries should not be written (they will cause infinite loops). -.. warning:: - - Some unsupported types such as subclasses of builtins will appear to marshal - and unmarshal correctly, but in fact, their type will change and the - additional subclass functionality and instance attributes will be lost. - .. warning:: On machines where C's ``long int`` type has more than 32 bits (such as the diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 656fc1facc7..1b2c8b71629 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -244,6 +244,17 @@ class BugsTestCase(unittest.TestCase): last.append([0]) self.assertRaises(ValueError, marshal.dumps, head) + def test_exact_type_match(self): + # Former bug: + # >>> class Int(int): pass + # >>> type(loads(dumps(Int()))) + # + for typ in (int, long, float, complex, tuple, list, dict, set, frozenset): + # Note: str and unicode sublclasses are not tested because they get handled + # by marshal's routines for objects supporting the buffer API. + subtyp = type('subtyp', (typ,), {}) + self.assertRaises(ValueError, marshal.dumps, subtyp()) + def test_main(): test_support.run_unittest(IntTestCase, FloatTestCase, diff --git a/Misc/NEWS b/Misc/NEWS index 502b18253fa..997a49a1469 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -812,6 +812,10 @@ Library Extension Modules ----------------- +- Marshal.dumps() now expects exact type matches for int, long, float, complex, + tuple, list, dict, set, and frozenset. Formerly, it would silently miscode + subclasses of those types. Now, it raises a ValueError instead. + - Patch #1388440: Add set_completion_display_matches_hook and get_completion_type to readline. diff --git a/Python/marshal.c b/Python/marshal.c index 897c15ec8ad..0c611b618c2 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -144,7 +144,7 @@ w_object(PyObject *v, WFILE *p) else if (v == Py_True) { w_byte(TYPE_TRUE, p); } - else if (PyInt_Check(v)) { + else if (PyInt_CheckExact(v)) { long x = PyInt_AS_LONG((PyIntObject *)v); #if SIZEOF_LONG > 4 long y = Py_ARITHMETIC_RIGHT_SHIFT(long, x, 31); @@ -159,7 +159,7 @@ w_object(PyObject *v, WFILE *p) w_long(x, p); } } - else if (PyLong_Check(v)) { + else if (PyLong_CheckExact(v)) { PyLongObject *ob = (PyLongObject *)v; w_byte(TYPE_LONG, p); n = ob->ob_size; @@ -169,7 +169,7 @@ w_object(PyObject *v, WFILE *p) for (i = 0; i < n; i++) w_short(ob->ob_digit[i], p); } - else if (PyFloat_Check(v)) { + else if (PyFloat_CheckExact(v)) { if (p->version > 1) { unsigned char buf[8]; if (_PyFloat_Pack8(PyFloat_AsDouble(v), @@ -190,7 +190,7 @@ w_object(PyObject *v, WFILE *p) } } #ifndef WITHOUT_COMPLEX - else if (PyComplex_Check(v)) { + else if (PyComplex_CheckExact(v)) { if (p->version > 1) { unsigned char buf[8]; if (_PyFloat_Pack8(PyComplex_RealAsDouble(v), @@ -236,7 +236,7 @@ w_object(PyObject *v, WFILE *p) } } #endif - else if (PyString_Check(v)) { + else if (PyString_CheckExact(v)) { if (p->strings && PyString_CHECK_INTERNED(v)) { PyObject *o = PyDict_GetItem(p->strings, v); if (o) { @@ -273,7 +273,7 @@ w_object(PyObject *v, WFILE *p) w_string(PyString_AS_STRING(v), (int)n, p); } #ifdef Py_USING_UNICODE - else if (PyUnicode_Check(v)) { + else if (PyUnicode_CheckExact(v)) { PyObject *utf8; utf8 = PyUnicode_AsUTF8String(v); if (utf8 == NULL) { @@ -293,7 +293,7 @@ w_object(PyObject *v, WFILE *p) Py_DECREF(utf8); } #endif - else if (PyTuple_Check(v)) { + else if (PyTuple_CheckExact(v)) { w_byte(TYPE_TUPLE, p); n = PyTuple_Size(v); w_long((long)n, p); @@ -301,7 +301,7 @@ w_object(PyObject *v, WFILE *p) w_object(PyTuple_GET_ITEM(v, i), p); } } - else if (PyList_Check(v)) { + else if (PyList_CheckExact(v)) { w_byte(TYPE_LIST, p); n = PyList_GET_SIZE(v); w_long((long)n, p); @@ -309,7 +309,7 @@ w_object(PyObject *v, WFILE *p) w_object(PyList_GET_ITEM(v, i), p); } } - else if (PyDict_Check(v)) { + else if (PyDict_CheckExact(v)) { Py_ssize_t pos; PyObject *key, *value; w_byte(TYPE_DICT, p); @@ -321,7 +321,7 @@ w_object(PyObject *v, WFILE *p) } w_object((PyObject *)NULL, p); } - else if (PyAnySet_Check(v)) { + else if (PyAnySet_CheckExact(v)) { PyObject *value, *it; if (PyObject_TypeCheck(v, &PySet_Type))