]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-99110: Initialize `frame->previous` in init_frame to fix segmentation fault when...
authorBill Fisher <william.w.fisher@gmail.com>
Fri, 23 Dec 2022 14:45:53 +0000 (07:45 -0700)
committerGitHub <noreply@github.com>
Fri, 23 Dec 2022 14:45:53 +0000 (20:15 +0530)
Include/internal/pycore_frame.h
Lib/test/test_frame.py
Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/frameobject.c

index 7fa410d288c33a399022d3c362da49dd042c467c..f18723b303224f3976119c78739e3eaa05e212b1 100644 (file)
@@ -96,7 +96,10 @@ static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, PyObject *value) {
 
 void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest);
 
-/* Consumes reference to func and locals */
+/* 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,
index ed413f105e5b170f2a0c3aea2dbd0ca73ec3c1a0..40c734b6e33abeb57730e2a890e3295fe3d2ab8c 100644 (file)
@@ -408,6 +408,15 @@ class TestCAPI(unittest.TestCase):
         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)
 
 if __name__ == "__main__":
     unittest.main()
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 (file)
index 0000000..175740d
--- /dev/null
@@ -0,0 +1,2 @@
+Initialize frame->previous in frameobject.c to fix a segmentation fault when
+accessing frames created by :c:func:`PyFrame_New`.
index 35c895d9ceb2a1c9b9945154fbeaabe2992abe49..c32fdb5f5fbefe7b14df0c8f7b390c4469b26cfa 100644 (file)
@@ -20,6 +20,7 @@
 #define PY_SSIZE_T_CLEAN
 
 #include "Python.h"
+#include "frameobject.h"          // PyFrame_New
 #include "marshal.h"              // PyMarshal_WriteLongToFile
 #include "structmember.h"         // for offsetof(), T_OBJECT
 #include <float.h>                // FLT_MAX
@@ -2839,6 +2840,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 *
 test_frame_getvar(PyObject *self, PyObject *args)
 {
@@ -3277,6 +3294,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},
     {"frame_getvar", test_frame_getvar, METH_VARARGS, NULL},
     {"frame_getvarstring", test_frame_getvarstring, METH_VARARGS, NULL},
     {"eval_get_func_name", eval_get_func_name, METH_O, NULL},
index 74c26d8d4d96a5e692e0129cb0aa1b5974f85039..eab85c08fc01655f134a602203c4e977f4a41ae7 100644 (file)
@@ -1013,6 +1013,7 @@ init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)
     PyCodeObject *code = (PyCodeObject *)func->func_code;
     _PyFrame_InitializeSpecials(frame, (PyFunctionObject*)Py_NewRef(func),
                                 Py_XNewRef(locals), code);
+    frame->previous = NULL;
     for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) {
         frame->localsplus[i] = NULL;
     }