Skip to content

[3.13] gh-148660: Fix use-after-free in OrderedDict.copy() on reentrant mutation (GH-151573)#152542

Merged
gpshead merged 1 commit into
python:3.13from
gpshead:backport-7d128e3-3.13
Jun 29, 2026
Merged

[3.13] gh-148660: Fix use-after-free in OrderedDict.copy() on reentrant mutation (GH-151573)#152542
gpshead merged 1 commit into
python:3.13from
gpshead:backport-7d128e3-3.13

Conversation

@gpshead

@gpshead gpshead commented Jun 29, 2026

Copy link
Copy Markdown
Member

OrderedDict.copy() walks the internal linked list while building the new
dict. The loop body can run arbitrary Python (a key's __eq__/__hash__, or
a subclass __getitem__/__setitem__) which can clear the source dict and
free the nodes being iterated.

Detect this the same way OrderedDict.__eq__ already does (gh-119004):
snapshot od_state before the loop, hold a strong reference to the key and
read the hash before any reentrant call, and raise RuntimeError if the
state changed before advancing to the next node.

(cherry picked from commit 7d128e3)


Manual backport notes (3.13): The test additions and NEWS entry applied cleanly; only Objects/odictobject.c needed adaptation because 3.13's odict_copy is hand-written (not Argument Clinic) and predates the free-threading refactor. The cherry-pick conflict was resolved to 3.13's idioms: od is already typed PyODictObject *, so _PyODictObject_CAST(od)->od_state becomes od->od_state; the lock-held insert helper _PyODict_SetItem_KnownHash_LockHeld() becomes the plain _PyODict_SetItem_KnownHash(); and (PyObject *)od casts were added on the PyODict_GetItemWithError()/PyObject_GetItem() calls. The fix logic is otherwise identical. test_ordered_dict passes in full (run=299), including the new test_issue148660_* cases on both the C and pure-Python OrderedDict variants.

…eentrant mutation (pythonGH-151573)

OrderedDict.copy() walks the internal linked list while building the new
dict. The loop body can run arbitrary Python (a key's __eq__/__hash__, or
a subclass __getitem__/__setitem__) which can clear the source dict and
free the nodes being iterated.

Detect this the same way OrderedDict.__eq__ already does (pythongh-119004):
snapshot od_state before the loop, hold a strong reference to the key and
read the hash before any reentrant call, and raise RuntimeError if the
state changed before advancing to the next node.

(cherry picked from commit 7d128e3)
@gpshead gpshead marked this pull request as ready for review June 29, 2026 02:54
@gpshead gpshead merged commit 9dc1ae0 into python:3.13 Jun 29, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant