Skip to content

Commit

Permalink
pythongh-99110: Initialize frame->previous in init_frame to fix seg…
Browse files Browse the repository at this point in the history
…mentation fault when accessing `frame.f_back` (python#100182)

Backport test_frame_fback_api from TestCAPI to Test_FrameAPI.

(cherry picked from commit 88d565f)
  • Loading branch information
byllyfish committed Dec 23, 2022
1 parent d5eb2f4 commit f5f8361
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 1 deletion.
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

0 comments on commit f5f8361

Please sign in to comment.