Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 4 additions & 1 deletion Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, PyObject *value) {

void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest);

/* Consumes reference to func */
/* Consumes reference to func and locals.
Does not initialize frame->previous, which happens
when frame is linked into the frame stack.
*/
static inline void
_PyFrame_InitializeSpecials(
_PyInterpreterFrame *frame, PyFunctionObject *func,
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,16 @@ def test_frame_get_generator(self):
frame = next(gen)
self.assertIs(gen, _testcapi.frame_getgenerator(frame))

def test_frame_fback_api(self):
"""Test that accessing `f_back` does not cause a segmentation fault on
a frame created with `PyFrame_New` (GH-99110)."""
def dummy():
pass

frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
# The following line should not cause a segmentation fault.
self.assertIsNone(frame.f_back)


SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Initialize frame->previous in frameobject.c to fix a segmentation fault when
accessing frames created by :c:func:`PyFrame_New`.
18 changes: 18 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "Python.h"
#include "datetime.h" // PyDateTimeAPI
#include "frameobject.h" // PyFrame_New
#include "marshal.h" // PyMarshal_WriteLongToFile
#include "structmember.h" // PyMemberDef
#include <float.h> // FLT_MAX
Expand Down Expand Up @@ -6000,6 +6001,22 @@ frame_getlasti(PyObject *self, PyObject *frame)
return PyLong_FromLong(lasti);
}

static PyObject *
frame_new(PyObject *self, PyObject *args)
{
PyObject *code, *globals, *locals;
if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) {
return NULL;
}
if (!PyCode_Check(code)) {
PyErr_SetString(PyExc_TypeError, "argument must be a code object");
return NULL;
}
PyThreadState *tstate = PyThreadState_Get();

return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals);
}

static PyObject *
eval_get_func_name(PyObject *self, PyObject *func)
{
Expand Down Expand Up @@ -6492,6 +6509,7 @@ static PyMethodDef TestMethods[] = {
{"frame_getgenerator", frame_getgenerator, METH_O, NULL},
{"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
{"frame_getlasti", frame_getlasti, METH_O, NULL},
{"frame_new", frame_new, METH_VARARGS, NULL},
{"eval_get_func_name", eval_get_func_name, METH_O, NULL},
{"eval_get_func_desc", eval_get_func_desc, METH_O, NULL},
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
Expand Down
1 change: 1 addition & 0 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,7 @@ init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)
Py_INCREF(func);
PyCodeObject *code = (PyCodeObject *)func->func_code;
_PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);
frame->previous = NULL;
for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) {
frame->localsplus[i] = NULL;
}
Expand Down