[Python-checkins] [3.11] gh-99110: Initialize frame->previous in init_frame to fix segmentation fault (GH-100182) (#100478)

kumaraditya303 webhook-mailer at python.org
Sat Dec 24 00:47:16 EST 2022


https://github.com/python/cpython/commit/57e727af3fda446dc79d65e2d17297d1194892ed
commit: 57e727af3fda446dc79d65e2d17297d1194892ed
branch: 3.11
author: Bill Fisher <william.w.fisher at gmail.com>
committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com>
date: 2022-12-24T11:17:10+05:30
summary:

[3.11] gh-99110: Initialize frame->previous in init_frame to fix segmentation fault (GH-100182) (#100478)

(cherry picked from commit 88d565f32a709140664444c6dea20ecd35a10e94)

Co-authored-by: Bill Fisher <william.w.fisher at gmail.com>

files:
A Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst
M Include/internal/pycore_frame.h
M Lib/test/test_capi/test_misc.py
M Modules/_testcapimodule.c
M Objects/frameobject.c

diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index ecc8346f5ef0..4866ea21b998 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -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,
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index 6cda91677054..db7a77416219 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -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
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst
new file mode 100644
index 000000000000..175740dfca07
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst	
@@ -0,0 +1,2 @@
+Initialize frame->previous in frameobject.c to fix a segmentation fault when
+accessing frames created by :c:func:`PyFrame_New`.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 70242d78ab8c..bb7de220a2db 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -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
@@ -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)
 {
@@ -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},
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index f77f5ed39392..1e6bdcfb592d 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -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;
     }



More information about the Python-checkins mailing list