Skip to content

Commit d267d72

Browse files
committed
gh-151830: Raise ValueError instead of SystemError for invalid code objects in marshal.loads()
1 parent 1b9fe5c commit d267d72

3 files changed

Lines changed: 29 additions & 0 deletions

File tree

Lib/test/test_marshal.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import array
55
import io
66
import marshal
7+
import struct
78
import sys
89
import unittest
910
import os
@@ -140,6 +141,27 @@ def test_different_filenames(self):
140141
self.assertEqual(co1.co_filename, "f1")
141142
self.assertEqual(co2.co_filename, "f2")
142143

144+
def test_inconsistent_code_object(self):
145+
# len(localsplusnames) must equal len(localspluskinds); marshal data that
146+
# breaks this must raise ValueError, not a leaked SystemError (gh-151830).
147+
co = compile("def f(a, b):\n x = a\n", "<test>", "exec").co_consts[0]
148+
n = len(co.co_varnames) + len(co.co_cellvars) + len(co.co_freevars)
149+
blob = marshal.dumps(co)
150+
# Find the localspluskinds record: the TYPE_STRING ('s') whose payload
151+
# is exactly n bytes long (one kind byte per localsplus name).
152+
for off in range(len(blob) - 5):
153+
if blob[off] == ord('s'):
154+
if struct.unpack_from('<i', blob, off + 1)[0] == n:
155+
kinds = blob[off + 5:off + 5 + n]
156+
break
157+
else:
158+
self.fail("could not locate localspluskinds in marshal data")
159+
# Rewrite it with one fewer kind byte than there are names.
160+
corrupt = (blob[:off] + b's' + struct.pack('<i', n - 1)
161+
+ kinds[:n - 1] + blob[off + 5 + n:])
162+
with self.assertRaises(ValueError):
163+
marshal.loads(corrupt)
164+
143165
def test_no_allow_code(self):
144166
data = {'a': [({0},)]}
145167
dump = marshal.dumps(data, allow_code=False)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed :func:`marshal.loads` raising :exc:`SystemError` instead of
2+
:exc:`ValueError` for serialized data containing a code object with
3+
inconsistent internal fields.

Python/marshal.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,10 @@ r_object(RFILE *p)
16701670
};
16711671

16721672
if (_PyCode_Validate(&con) < 0) {
1673+
if (PyErr_ExceptionMatches(PyExc_SystemError)) {
1674+
PyErr_SetString(PyExc_ValueError,
1675+
"bad marshal data (invalid code object)");
1676+
}
16731677
goto code_error;
16741678
}
16751679

0 commit comments

Comments
 (0)