gh-144446: Fix some frame object thread-safety issues (gh-144479)

Fix thread-safety issues when accessing frame attributes while another
thread is executing the frame:

- Add critical section to frame_repr() to prevent races when accessing
  the frame's code object and line number

- Add _Py_NO_SANITIZE_THREAD to PyUnstable_InterpreterFrame_GetLasti()
  to allow intentional racy reads of instr_ptr.

- Fix take_ownership() to not write to the original frame's f_executable
This commit is contained in:
Sam Gross 2026-02-06 09:43:36 -05:00 committed by GitHub
parent 45d4a34720
commit 5bb3bbb9c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 164 additions and 7 deletions

View file

@ -54,7 +54,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
_PyFrame_Copy(frame, new_frame);
// _PyFrame_Copy takes the reference to the executable,
// so we need to restore it.
frame->f_executable = PyStackRef_DUP(new_frame->f_executable);
new_frame->f_executable = PyStackRef_DUP(new_frame->f_executable);
f->f_frame = new_frame;
new_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
if (_PyFrame_IsIncomplete(new_frame)) {
@ -135,14 +135,14 @@ PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame)
return PyStackRef_AsPyObjectNew(frame->f_executable);
}
int
// NOTE: We allow racy accesses to the instruction pointer from other threads
// for sys._current_frames() and similar APIs.
int _Py_NO_SANITIZE_THREAD
PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame)
{
return _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
}
// NOTE: We allow racy accesses to the instruction pointer from other threads
// for sys._current_frames() and similar APIs.
int _Py_NO_SANITIZE_THREAD
PyUnstable_InterpreterFrame_GetLine(_PyInterpreterFrame *frame)
{