[Python-checkins] cpython (2.7): Issue #23914: Fixed SystemError raised by unpickler on broken pickle data.

serhiy.storchaka python-checkins at python.org
Mon Nov 23 08:21:10 EST 2015


https://hg.python.org/cpython/rev/686fa9439d38
changeset:   99308:686fa9439d38
branch:      2.7
parent:      99298:e3dea2e4f93d
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Mon Nov 23 15:20:43 2015 +0200
summary:
  Issue #23914: Fixed SystemError raised by unpickler on broken pickle data.

files:
  Lib/test/pickletester.py |  74 +++++++++++++++++++++++++++-
  Lib/test/test_cpickle.py |   2 +
  Lib/test/test_pickle.py  |   3 +-
  Misc/NEWS                |   1 +
  Modules/cPickle.c        |  24 ++++++++-
  5 files changed, 100 insertions(+), 4 deletions(-)


diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -7,7 +7,7 @@
 import pickletools
 import copy_reg
 
-from test.test_support import TestFailed, verbose, have_unicode, TESTFN
+from test.test_support import TestFailed, verbose, have_unicode, TESTFN, captured_stdout
 try:
     from test.test_support import _2G, _1M, precisionbigmemtest
 except ImportError:
@@ -634,6 +634,78 @@
         self.assertEqual(unpickled, ([],)*2)
         self.assertIs(unpickled[0], unpickled[1])
 
+    def test_bad_stack(self):
+        badpickles = [
+            b'0.',              # POP
+            b'1.',              # POP_MARK
+            b'2.',              # DUP
+            # b'(2.',           # PyUnpickler doesn't raise
+            b'R.',              # REDUCE
+            b')R.',
+            b'a.',              # APPEND
+            b'Na.',
+            b'b.',              # BUILD
+            b'Nb.',
+            b'd.',              # DICT
+            b'e.',              # APPENDS
+            # b'(e.',           # PyUnpickler raises AttributeError
+            b'i__builtin__\nlist\n.',  # INST
+            b'l.',              # LIST
+            b'o.',              # OBJ
+            b'(o.',
+            b'p1\n.',           # PUT
+            b'q\x00.',          # BINPUT
+            b'r\x00\x00\x00\x00.',  # LONG_BINPUT
+            b's.',              # SETITEM
+            b'Ns.',
+            b'NNs.',
+            b't.',              # TUPLE
+            b'u.',              # SETITEMS
+            b'(u.',
+            b'}(Nu.',
+            b'\x81.',           # NEWOBJ
+            b')\x81.',
+            b'\x85.',           # TUPLE1
+            b'\x86.',           # TUPLE2
+            b'N\x86.',
+            b'\x87.',           # TUPLE3
+            b'N\x87.',
+            b'NN\x87.',
+        ]
+        for p in badpickles:
+            try:
+                self.assertRaises(self.bad_stack_errors, self.loads, p)
+            except:
+                print '***', repr(p)
+                raise
+
+    def test_bad_mark(self):
+        badpickles = [
+            b'c__builtin__\nlist\n)(R.',        # REDUCE
+            b'c__builtin__\nlist\n()R.',
+            b']N(a.',                           # APPEND
+            b'cexceptions\nValueError\n)R}(b.',  # BUILD
+            b'cexceptions\nValueError\n)R(}b.',
+            b'(Nd.',                            # DICT
+            b'}NN(s.',                          # SETITEM
+            b'}N(Ns.',
+            b'c__builtin__\nlist\n)(\x81.',     # NEWOBJ
+            b'c__builtin__\nlist\n()\x81.',
+            b'N(\x85.',                         # TUPLE1
+            b'NN(\x86.',                        # TUPLE2
+            b'N(N\x86.',
+            b'NNN(\x87.',                       # TUPLE3
+            b'NN(N\x87.',
+            b'N(NN\x87.',
+        ]
+        for p in badpickles:
+            # PyUnpickler prints reduce errors to stdout
+            try:
+                self.loads(p)
+            except (IndexError, AttributeError, TypeError,
+                    pickle.UnpicklingError):
+                pass
+
 
 class AbstractPickleTests(unittest.TestCase):
     # Subclass must define self.dumps, self.loads.
diff --git a/Lib/test/test_cpickle.py b/Lib/test/test_cpickle.py
--- a/Lib/test/test_cpickle.py
+++ b/Lib/test/test_cpickle.py
@@ -51,6 +51,7 @@
 
     error = cPickle.BadPickleGet
     module = cPickle
+    bad_stack_errors = (cPickle.UnpicklingError,)
 
 class cPickleUnpicklerTests(AbstractUnpickleTests):
 
@@ -63,6 +64,7 @@
             self.close(f)
 
     error = cPickle.BadPickleGet
+    bad_stack_errors = (cPickle.UnpicklingError,)
 
 class cStringIOCUnpicklerTests(cStringIOMixin, cPickleUnpicklerTests):
     pass
diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py
--- a/Lib/test/test_pickle.py
+++ b/Lib/test/test_pickle.py
@@ -23,17 +23,18 @@
 
     module = pickle
     error = KeyError
+    bad_stack_errors = (IndexError,)
 
 class UnpicklerTests(AbstractUnpickleTests):
 
     error = KeyError
+    bad_stack_errors = (IndexError,)
 
     def loads(self, buf):
         f = StringIO(buf)
         u = pickle.Unpickler(f)
         return u.load()
 
-
 class PicklerTests(AbstractPickleTests):
 
     def dumps(self, arg, proto=0, fast=0):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,7 @@
 Library
 -------
 
+- Issue #23914: Fixed SystemError raised by CPickle unpickler on broken data.
 
 What's New in Python 2.7.11?
 ============================
diff --git a/Modules/cPickle.c b/Modules/cPickle.c
--- a/Modules/cPickle.c
+++ b/Modules/cPickle.c
@@ -3945,6 +3945,10 @@
     Py_ssize_t i;
 
     if ((i = marker(self)) < 0) return -1;
+
+    if (self->stack->length - i < 1)
+        return stackUnderflow();
+
     if (!( tup=Pdata_popTuple(self->stack, i+1)))  return -1;
     PDATA_POP(self->stack, class);
     if (class) {
@@ -4496,6 +4500,8 @@
 static int
 load_append(Unpicklerobject *self)
 {
+    if (self->stack->length - 1 <= 0)
+        return stackUnderflow();
     return do_append(self, self->stack->length - 1);
 }
 
@@ -4503,7 +4509,10 @@
 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);
 }
 
 
@@ -4515,6 +4524,14 @@
 
     if (!( (len=self->stack->length) >= x
            && x > 0 ))  return stackUnderflow();
+    if (len == x)  /* nothing to do */
+        return 0;
+    if ((len - x) % 2 != 0) {
+        /* Currupt or hostile pickle -- we never write one like this. */
+        PyErr_SetString(UnpicklingError,
+                        "odd number of items for SETITEMS");
+        return -1;
+    }
 
     dict=self->stack->data[x-1];
 
@@ -4542,7 +4559,10 @@
 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);
 }
 
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list