Skip to content

Commit 57e727a

Browse files
authored
[3.11] gh-99110: Initialize frame->previous in init_frame to fix segmentation fault (GH-100182) (#100478)
(cherry picked from commit 88d565f) Co-authored-by: Bill Fisher <william.w.fisher@gmail.com>
1 parent b914054 commit 57e727a

File tree

5 files changed

+35
-1
lines changed

5 files changed

+35
-1
lines changed

Include/internal/pycore_frame.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,10 @@ static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, PyObject *value) {
9494

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

97-
/* Consumes reference to func */
97+
/* Consumes reference to func and locals.
98+
Does not initialize frame->previous, which happens
99+
when frame is linked into the frame stack.
100+
*/
98101
static inline void
99102
_PyFrame_InitializeSpecials(
100103
_PyInterpreterFrame *frame, PyFunctionObject *func,

Lib/test/test_capi/test_misc.py

+10
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,16 @@ def test_frame_get_generator(self):
13351335
frame = next(gen)
13361336
self.assertIs(gen, _testcapi.frame_getgenerator(frame))
13371337

1338+
def test_frame_fback_api(self):
1339+
"""Test that accessing `f_back` does not cause a segmentation fault on
1340+
a frame created with `PyFrame_New` (GH-99110)."""
1341+
def dummy():
1342+
pass
1343+
1344+
frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
1345+
# The following line should not cause a segmentation fault.
1346+
self.assertIsNone(frame.f_back)
1347+
13381348

13391349
SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
13401350

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Initialize frame->previous in frameobject.c to fix a segmentation fault when
2+
accessing frames created by :c:func:`PyFrame_New`.

Modules/_testcapimodule.c

+18
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "Python.h"
2424
#include "datetime.h" // PyDateTimeAPI
25+
#include "frameobject.h" // PyFrame_New
2526
#include "marshal.h" // PyMarshal_WriteLongToFile
2627
#include "structmember.h" // PyMemberDef
2728
#include <float.h> // FLT_MAX
@@ -6000,6 +6001,22 @@ frame_getlasti(PyObject *self, PyObject *frame)
60006001
return PyLong_FromLong(lasti);
60016002
}
60026003

6004+
static PyObject *
6005+
frame_new(PyObject *self, PyObject *args)
6006+
{
6007+
PyObject *code, *globals, *locals;
6008+
if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) {
6009+
return NULL;
6010+
}
6011+
if (!PyCode_Check(code)) {
6012+
PyErr_SetString(PyExc_TypeError, "argument must be a code object");
6013+
return NULL;
6014+
}
6015+
PyThreadState *tstate = PyThreadState_Get();
6016+
6017+
return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals);
6018+
}
6019+
60036020
static PyObject *
60046021
eval_get_func_name(PyObject *self, PyObject *func)
60056022
{
@@ -6492,6 +6509,7 @@ static PyMethodDef TestMethods[] = {
64926509
{"frame_getgenerator", frame_getgenerator, METH_O, NULL},
64936510
{"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
64946511
{"frame_getlasti", frame_getlasti, METH_O, NULL},
6512+
{"frame_new", frame_new, METH_VARARGS, NULL},
64956513
{"eval_get_func_name", eval_get_func_name, METH_O, NULL},
64966514
{"eval_get_func_desc", eval_get_func_desc, METH_O, NULL},
64976515
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},

Objects/frameobject.c

+1
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,7 @@ init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)
10101010
Py_INCREF(func);
10111011
PyCodeObject *code = (PyCodeObject *)func->func_code;
10121012
_PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);
1013+
frame->previous = NULL;
10131014
for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) {
10141015
frame->localsplus[i] = NULL;
10151016
}

0 commit comments

Comments
 (0)