-
-
Notifications
You must be signed in to change notification settings - Fork 30.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-106092: Fix use-after-free crash in frame_dealloc #106875
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 <[email protected]>
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <[email protected]> Signed-off-by: Anders Kaseorg <[email protected]>
…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 <[email protected]> Signed-off-by: Anders Kaseorg <[email protected]>
… (#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 <[email protected]> Co-authored-by: Anders Kaseorg <[email protected]>
It was possible for the trashcan to delay the deallocation of a
PyFrameObject
until after its corresponding_PyInterpreterFrame
has already been freed. Soframe_dealloc
needs to avoid dereferencing thef_frame
pointer unless it first checks that the pointer still points to valid memory.Fixes #106092. This should be backported to 3.11 and 3.12.