Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Make accesses to a frame's f_locals safe from C code, not relying o…
…n calls to `PyFrame_FastToLocals` or `PyFrame_LocalsToFast`.
  • Loading branch information
markshannon committed Mar 23, 2022
commit 4bc0707bfb72362fd154e07093293d2071f066c3
1 change: 1 addition & 0 deletions Include/cpython/frameobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f);
PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);

PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame);
1 change: 1 addition & 0 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct _frame {
int f_lineno; /* Current line number. Only valid if non-zero */
char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */
char f_fast_as_locals; /* Have the fast locals of this frame been converted to a dict? */
/* The frame data, if this frame object owns the frame */
PyObject *_f_frame_data[1];
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
``PyFrame_FastToLocalsWithError`` and ``PyFrame_LocalsToFast`` are no longer
called during profile nor tracing. C code that accesses the ``f_locals`` attribute
directly (which is not recommended) must now call
``PyFrame_FastToLocalsWithError`` to update locals and ``PyFrame_LocalsToFast``
after ``f_locals`` are updated.
directly must do so by calling ``PyFrame_GetLocals``.
29 changes: 23 additions & 6 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,7 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
f->f_trace = NULL;
f->f_trace_lines = 1;
f->f_trace_opcodes = 0;
f->f_fast_as_locals = 0;
f->f_lineno = 0;
return f;
}
Expand Down Expand Up @@ -911,6 +912,9 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) {
PyObject **fast;
PyCodeObject *co;
locals = frame->f_locals;
if ((frame->f_code->co_flags & CO_NEWLOCALS) == 0) {
return 0;
}
if (locals == NULL) {
locals = frame->f_locals = PyDict_New();
if (locals == NULL)
Expand Down Expand Up @@ -1004,7 +1008,11 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f)
PyErr_BadInternalCall();
return -1;
}
return _PyFrame_FastToLocalsWithError(f->f_frame);
int err = _PyFrame_FastToLocalsWithError(f->f_frame);
if (err == 0) {
f->f_fast_as_locals = 1;
Comment thread
markshannon marked this conversation as resolved.
}
return err;
}

void
Expand All @@ -1028,8 +1036,12 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
PyObject *error_type, *error_value, *error_traceback;
PyCodeObject *co;
locals = frame->f_locals;
if (locals == NULL)
if ((frame->f_code->co_flags & CO_NEWLOCALS) == 0) {
return;
}
if (locals == NULL) {
return;
}
fast = _PyFrame_GetLocalsArray(frame);
co = frame->f_code;

Expand Down Expand Up @@ -1088,13 +1100,12 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
void
PyFrame_LocalsToFast(PyFrameObject *f, int clear)
{
if (f == NULL || _PyFrame_GetState(f) == FRAME_CLEARED) {
return;
if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) {
Comment thread
markshannon marked this conversation as resolved.
_PyFrame_LocalsToFast(f->f_frame, clear);
f->f_fast_as_locals = 0;
}
_PyFrame_LocalsToFast(f->f_frame, clear);
}


PyCodeObject *
PyFrame_GetCode(PyFrameObject *frame)
{
Expand All @@ -1118,6 +1129,12 @@ PyFrame_GetBack(PyFrameObject *frame)
return back;
}

PyObject*
PyFrame_GetLocals(PyFrameObject *frame)
Comment thread
markshannon marked this conversation as resolved.
{
return frame_getlocals(frame, NULL);
}

PyObject*
_PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals)
{
Expand Down
5 changes: 5 additions & 0 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,11 @@ call_trampoline(PyThreadState *tstate, PyObject* callback,
/* call the Python-level function */
PyObject *result = _PyObject_FastCallTstate(tstate, callback, stack, 3);

PyFrame_LocalsToFast(frame, 1);

if (result == NULL) {
PyTraceBack_Here(frame);
}

return result;
}
Expand Down