gh-106092: Fix use-after-free crash in frame_dealloc#106875
gh-106092: Fix use-after-free crash in frame_dealloc#106875markshannon merged 1 commit intopython:mainfrom
Conversation
It was possible for the trashcan to delay the deallocation of a PyFrameObject until after its corresponding _PyInterpreterFrame has already been freed. So frame_dealloc needs to avoid dereferencing the f_frame pointer unless it first checks that the pointer still points to valid memory. Signed-off-by: Anders Kaseorg <andersk@mit.edu>
|
I think it better to ensure that Do you have a reproducer that doesn't need third-party modules? |
Note that
Couldn’t that incorrectly skip the deallocation of
Not at present—further minimization is probably possible but difficult, because the segfault requires a specific coincidence of the position of the stack pointer within the current stack chunk, the GC threshold, and the trashcan recursion depth. |
|
A reproducer that causes an assertion failure would be good enough, I think. |
| if (f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { | ||
| assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data); | ||
| _PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data; | ||
| if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { |
There was a problem hiding this comment.
This looks correct.
Note:
In theory, f->f_frame == frame and frame->owner == FRAME_OWNED_BY_FRAME_OBJECT should be equivalent, but in practice they are not.
We can't just check frame->owner == FRAME_OWNED_BY_FRAME_OBJECT because of the Trashcan mechanism, and we can't just check f->f_frame == frame because of this.
So we need both checks here.
There was a problem hiding this comment.
Should this check be put in other places too? We are currently investigating the root cause of segfaults in 3.11.5 with call stacks ending with
#0 0x0000000000635d9a in ?? ()
#1 0x00000000004d8e27 in PyFrame_FastToLocalsWithError ()
#2 0x00000000004d8e3e in ?? ()
#3 0x000000000054e5f6 in _PyObject_GenericGetAttrWithDict ()
What we are trying to access here is frame.f_locals. The frame object is obtained from looping over all thread states and calling PyThreadState_GetFrame on them (so we know we have a strong reference to it). Because of the inlining it's not immediately obvious where in PyFrame_FastToLocalsWithError we are segfaulting exactly, but our current hunch is that it is because the referenced _PyInterpreterState might have been deallocated. We're still investigating this.
|
Thanks @andersk for the PR, and @markshannon for merging it 🌮🎉.. I'm working now to backport this PR to: 3.12. |
|
GH-107532 is a backport of this pull request to the 3.12 branch. |
|
Thanks @andersk for the PR, and @markshannon for merging it 🌮🎉.. I'm working now to backport this PR to: 3.11. |
|
GH-107533 is a backport of this pull request to the 3.11 branch. |
…106875) It was possible for the trashcan to delay the deallocation of a PyFrameObject until after its corresponding _PyInterpreterFrame has already been freed. So frame_dealloc needs to avoid dereferencing the f_frame pointer unless it first checks that the pointer still points to the interpreter frame within the frame object. (cherry picked from commit 557b05c) Co-authored-by: Anders Kaseorg <andersk@mit.edu> Signed-off-by: Anders Kaseorg <andersk@mit.edu>
…106875) It was possible for the trashcan to delay the deallocation of a PyFrameObject until after its corresponding _PyInterpreterFrame has already been freed. So frame_dealloc needs to avoid dereferencing the f_frame pointer unless it first checks that the pointer still points to the interpreter frame within the frame object. (cherry picked from commit 557b05c) Co-authored-by: Anders Kaseorg <andersk@mit.edu> Signed-off-by: Anders Kaseorg <andersk@mit.edu>
… (#107532) gh-106092: Fix use-after-free crash in frame_dealloc (GH-106875) It was possible for the trashcan to delay the deallocation of a PyFrameObject until after its corresponding _PyInterpreterFrame has already been freed. So frame_dealloc needs to avoid dereferencing the f_frame pointer unless it first checks that the pointer still points to the interpreter frame within the frame object. (cherry picked from commit 557b05c) Signed-off-by: Anders Kaseorg <andersk@mit.edu> Co-authored-by: Anders Kaseorg <andersk@mit.edu>
It was possible for the trashcan to delay the deallocation of a
PyFrameObjectuntil after its corresponding_PyInterpreterFramehas already been freed. Soframe_deallocneeds to avoid dereferencing thef_framepointer unless it first checks that the pointer still points to valid memory.Fixes #106092. This should be backported to 3.11 and 3.12.