gh-148653: Fix SIGSEGV in marshal.loads for self-referencing tuples

This commit is contained in:
Michael Bommarito 2026-04-16 13:40:35 -04:00
parent 2faceeec5c
commit 7c214ea52e
3 changed files with 26 additions and 1 deletions

View file

@ -731,5 +731,25 @@ def test_read_object_from_file(self):
os_helper.unlink(os_helper.TESTFN)
class SelfRefTupleTest(unittest.TestCase):
"""Regression test for gh-148653: TYPE_TUPLE with FLAG_REF back-reference.
R_REF registered the tuple in p->refs before its slots were populated.
A TYPE_REF back-reference to the partial tuple could reach a hashing
site (PySet_Add) with NULL slots, crashing with SIGSEGV.
The fix uses the two-phase r_ref_reserve/r_ref_insert pattern so the
Py_None placeholder is detected by the TYPE_REF handler, raising
ValueError instead.
"""
def test_self_ref_tuple(self):
# TYPE_TUPLE|FLAG_REF n=2; NONE; TYPE_SET n=1; TYPE_REF(0)
payload = (b'\xa8\x02\x00\x00\x00N'
b'<\x01\x00\x00\x00r\x00\x00\x00\x00')
with self.assertRaises(ValueError):
marshal.loads(payload)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,2 @@
Fix SIGSEGV in :func:`marshal.loads` for self-referencing tuples. Such
payloads will now raise :exc:`ValueError` instead of crashing.

View file

@ -1385,7 +1385,9 @@ r_object(RFILE *p)
}
_read_tuple:
v = PyTuple_New(n);
R_REF(v);
idx = r_ref_reserve(flag, p);
if (idx < 0)
Py_CLEAR(v);
if (v == NULL)
break;
@ -1400,6 +1402,7 @@ r_object(RFILE *p)
}
PyTuple_SET_ITEM(v, i, v2);
}
v = r_ref_insert(v, idx, flag, p);
retval = v;
break;