diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2011-08-29 21:14:53 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2011-08-29 21:14:53 (GMT) |
commit | ee763e2acc469fa2f423440517b9bc227fbbe79c (patch) | |
tree | 07daf6aa29d762ccc1c5f7fd1fc09196dcf1b13b /Lib/test/pickletester.py | |
parent | 466517df0e272f1b5d46d4e5eed112cefec3d7e3 (diff) | |
parent | 82be19f889e97618d73f405ad53ceffbee462008 (diff) | |
download | cpython-ee763e2acc469fa2f423440517b9bc227fbbe79c.zip cpython-ee763e2acc469fa2f423440517b9bc227fbbe79c.tar.gz cpython-ee763e2acc469fa2f423440517b9bc227fbbe79c.tar.bz2 |
Issue #11564: Avoid crashes when trying to pickle huge objects or containers
(more than 2**31 items). Instead, in most cases, an OverflowError is raised.
Diffstat (limited to 'Lib/test/pickletester.py')
-rw-r--r-- | Lib/test/pickletester.py | 101 |
1 files changed, 100 insertions, 1 deletions
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index e862d07..807221a 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2,11 +2,15 @@ import io import unittest import pickle import pickletools +import sys import copyreg import weakref from http.cookies import SimpleCookie -from test.support import TestFailed, TESTFN, run_with_locale, no_tracing +from test.support import ( + TestFailed, TESTFN, run_with_locale, no_tracing, + _2G, _4G, precisionbigmemtest, + ) from pickle import bytes_types @@ -15,6 +19,8 @@ from pickle import bytes_types # kind of outer loop. protocols = range(pickle.HIGHEST_PROTOCOL + 1) +character_size = 4 if sys.maxunicode > 0xFFFF else 2 + # Return True if opcode code appears in the pickle, else False. def opcode_in_pickle(code, pickle): @@ -1127,6 +1133,99 @@ class AbstractPickleTests(unittest.TestCase): if proto >= 2: self.assertLessEqual(sizes[-1], 14) + def check_negative_32b_binXXX(self, dumped): + if sys.maxsize > 2**32: + self.skipTest("test is only meaningful on 32-bit builds") + # XXX Pure Python pickle reads lengths as signed and passes + # them directly to read() (hence the EOFError) + with self.assertRaises((pickle.UnpicklingError, EOFError, + ValueError, OverflowError)): + self.loads(dumped) + + def test_negative_32b_binbytes(self): + # On 32-bit builds, a BINBYTES of 2**31 or more is refused + self.check_negative_32b_binXXX(b'\x80\x03B\xff\xff\xff\xffxyzq\x00.') + + def test_negative_32b_binunicode(self): + # On 32-bit builds, a BINUNICODE of 2**31 or more is refused + self.check_negative_32b_binXXX(b'\x80\x03X\xff\xff\xff\xffxyzq\x00.') + + +class BigmemPickleTests(unittest.TestCase): + + # Binary protocols can serialize longs of up to 2GB-1 + + @precisionbigmemtest(size=_2G, memuse=1 + 1, dry_run=False) + def test_huge_long_32b(self, size): + data = 1 << (8 * size) + try: + for proto in protocols: + if proto < 2: + continue + with self.assertRaises((ValueError, OverflowError)): + self.dumps(data, protocol=proto) + finally: + data = None + + # Protocol 3 can serialize up to 4GB-1 as a bytes object + # (older protocols don't have a dedicated opcode for bytes and are + # too inefficient) + + @precisionbigmemtest(size=_2G, memuse=1 + 1, dry_run=False) + def test_huge_bytes_32b(self, size): + data = b"abcd" * (size // 4) + try: + for proto in protocols: + if proto < 3: + continue + try: + pickled = self.dumps(data, protocol=proto) + self.assertTrue(b"abcd" in pickled[:15]) + self.assertTrue(b"abcd" in pickled[-15:]) + finally: + pickled = None + finally: + data = None + + @precisionbigmemtest(size=_4G, memuse=1 + 1, dry_run=False) + def test_huge_bytes_64b(self, size): + data = b"a" * size + try: + for proto in protocols: + if proto < 3: + continue + with self.assertRaises((ValueError, OverflowError)): + self.dumps(data, protocol=proto) + finally: + data = None + + # All protocols use 1-byte per printable ASCII character; we add another + # byte because the encoded form has to be copied into the internal buffer. + + @precisionbigmemtest(size=_2G, memuse=2 + character_size, dry_run=False) + def test_huge_str_32b(self, size): + data = "abcd" * (size // 4) + try: + for proto in protocols: + try: + pickled = self.dumps(data, protocol=proto) + self.assertTrue(b"abcd" in pickled[:15]) + self.assertTrue(b"abcd" in pickled[-15:]) + finally: + pickled = None + finally: + data = None + + @precisionbigmemtest(size=_4G, memuse=1 + character_size, dry_run=False) + def test_huge_str_64b(self, size): + data = "a" * size + try: + for proto in protocols: + with self.assertRaises((ValueError, OverflowError)): + self.dumps(data, protocol=proto) + finally: + data = None + # Test classes for reduce_ex |