diff -r 1412be96faf0 Lib/test/test_pickle.py --- a/Lib/test/test_pickle.py Sat Nov 14 15:42:17 2015 +0200 +++ b/Lib/test/test_pickle.py Sun Nov 15 00:56:46 2015 +0200 @@ -32,12 +32,96 @@ class PickleTests(AbstractPickleModuleTe class PyUnpicklerTests(AbstractUnpickleTests): unpickler = pickle._Unpickler + bad_stack_errors = (IndexError,) def loads(self, buf, **kwds): f = io.BytesIO(buf) u = self.unpickler(f, **kwds) return u.load() + def test_bad_stack(self): + badpickles = [ + b'0.', + b'1.', + b'2.', + # b'(2.', # PyUnpickler doesn't raise + b'R.', + b')R.', + b'a.', + b'Na.', + b'b.', + b'Nb.', + b'd.', + b'e.', + # b'(e.', # PyUnpickler raises AttributeError + b'ibuiltins\nlist\n.', + b'l.', + b'o.', + b'(o.', + b'p1\n.', + b'q\x00.', + b'r\x00\x00\x00\x00.', + b's.', + b'Ns.', + b'NNs.', + b't.', + b'u.', + b'(u.', + b'}(Nu.', + b'\x81.', + b')\x81.', + b'\x85.', + b'\x86.', + b'N\x86.', + b'\x87.', + b'N\x87.', + b'NN\x87.', + b'\x90.', + # b'(\x90.',, # PyUnpickler raises AttributeError + b'\x91.', + b'\x92.', + b')}\x92.', + b'\x93.', + b'Vlist\n\x93.', + b'\x94', + ] + for p in badpickles: + with self.subTest(p): + self.assertRaises(self.bad_stack_errors, self.loads, p) + + def test_bad_mark(self): + badpickles = [ + b'cbuiltins\nlist\n)(R.', + b'cbuiltins\nlist\n()R.', + b']N(a.', + b'cbuiltins\nValueError\n)R}(b.', + b'cbuiltins\nValueError\n)R(}b.', + b'(Nd.', + b'}NN(s.', + b'}N(Ns.', + b'cbuiltins\nlist\n)(\x81.', + b'cbuiltins\nlist\n()\x81.', + b'N(\x85.', + b'NN(\x86.', + b'N(N\x86.', + b'NNN(\x87.', + b'NN(N\x87.', + b'N(NN\x87.', + b'cbuiltins\nlist\n)}(\x92.', + b'cbuiltins\nlist\n)(}\x92.', + b'cbuiltins\nlist\n()}\x92.', + b'Vbuiltins\n(Vlist\n\x93.', + b'Vbuiltins\nVlist\n(\x93.', + ] + for p in badpickles: + with self.subTest(p), support.captured_stdout(): + # PyUnpickler prints reduce errors to stdout + try: + self.loads(p) + except (IndexError, AttributeError, TypeError, + pickle.UnpicklingError): + pass + class PyPicklerTests(AbstractPickleTests): @@ -119,6 +203,7 @@ class PyChainDispatchTableTests(Abstract if has_c_implementation: class CUnpicklerTests(PyUnpicklerTests): unpickler = _pickle.Unpickler + bad_stack_errors = (pickle.UnpicklingError,) class CPicklerTests(PyPicklerTests): pickler = _pickle.Pickler diff -r 1412be96faf0 Modules/_pickle.c --- a/Modules/_pickle.c Sat Nov 14 15:42:17 2015 +0200 +++ b/Modules/_pickle.c Sun Nov 15 00:56:46 2015 +0200 @@ -472,8 +472,8 @@ Pdata_grow(Pdata *self) static PyObject * Pdata_pop(Pdata *self) { - PickleState *st = _Pickle_GetGlobalState(); if (Py_SIZE(self) == 0) { + PickleState *st = _Pickle_GetGlobalState(); PyErr_SetString(st->UnpicklingError, "bad pickle data"); return NULL; } @@ -5210,6 +5210,9 @@ load_obj(UnpicklerObject *self) if ((i = marker(self)) < 0) return -1; + if (Py_SIZE(self->stack) - i <= 1) + return stack_underflow(); + args = Pdata_poptuple(self->stack, i + 1); if (args == NULL) return -1; @@ -5868,13 +5871,18 @@ do_append(UnpicklerObject *self, Py_ssiz static int load_append(UnpicklerObject *self) { + if (Py_SIZE(self->stack) - 1 <= 0) + return stack_underflow(); return do_append(self, Py_SIZE(self->stack) - 1); } static int load_appends(UnpicklerObject *self) { - return do_append(self, marker(self)); + Py_ssize_t i = marker(self); + if (i < 0) + return -1; + return do_append(self, i); } static int @@ -5924,7 +5932,10 @@ load_setitem(UnpicklerObject *self) static int load_setitems(UnpicklerObject *self) { - return do_setitems(self, marker(self)); + Py_ssize_t i = marker(self); + if (i < 0) + return -1; + return do_setitems(self, i); } static int @@ -5934,6 +5945,8 @@ load_additems(UnpicklerObject *self) Py_ssize_t mark, len, i; mark = marker(self); + if (mark < 0) + return -1; len = Py_SIZE(self->stack); if (mark > len || mark <= 0) return stack_underflow();