Repository navigation
Use after free in PyDict_Clear() due to re-entrancy #130555
Copy link
Copy link
Closed
Labels
3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Description
Activity
- added3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
on Feb 25, 2025 I don't see a way to fix this without copying
oldvaluesto a temporary array.We should detach the object from the dictionary when the object is freed (which should involve copying the values and assigning a new values).
So couldn't we check in the clear loop if the values have changed from
oldvaluesand if so then start over with the newoldvalues? Maybe only do that once in the embedded case as there's probably also recursion issues where clearing inserts into the dictionary and causes it to resize :PThat might work, but I'm a bit unsure about it:
- The
list.clear(),set.clear(), anddict.clear()prior to 3.13 ensured that the container was empty before decref'ing any previous elements. That makes the clear more atomic both with respect to re-entrancy and other threads. oldvaluesmay be a dangling pointer, in which case a check likeoldvalues != mp->ma_valueswould technically be undefined behavior. I'm also not sure what would happen if the newmp->ma_valueshappened to be allocated at the same address as the now freedoldvalues.- The dict seems like it'd be in an inconsistent state: do we set
PyDictValues.sizeto zero before or after we start clearing elements?
- The
Here is another reproducer:
class F: pass f = F() f.f = f f.c = "c" d = f.__dict__ del f d.clear()
- added 7 commits that reference this issue
on Feb 26, 2026
Metadata
Metadata
Assignees
Labels
3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Crash report
The implementation of
PyDict_Clear()isn't safe for dictionaries with values that are embedded in an object:The calls to
Py_CLEAR(oldvalues->values[i])can execute arbitrary code via object destructors.oldvaluesmay no longer be valid because the object its embedded in may be freed during thePy_CLEAR()call.cpython/Objects/dictobject.c
Lines 2868 to 2880 in 0ef4ffe
Repro
cc @DinoV @markshannon
Linked PRs