summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/internal/pycore_frame.h5
-rw-r--r--Lib/test/test_frame.py9
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst2
-rw-r--r--Modules/_testcapimodule.c18
-rw-r--r--Objects/frameobject.c1
5 files changed, 34 insertions, 1 deletions
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 7fa410d..f18723b 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -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,
diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py
index ed413f1..40c734b 100644
--- a/Lib/test/test_frame.py
+++ b/Lib/test/test_frame.py
@@ -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
index 0000000..175740d
--- /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 35c895d..c32fdb5 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -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
@@ -2840,6 +2841,22 @@ frame_getlasti(PyObject *self, PyObject *frame)
}
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)
{
PyObject *frame, *name;
@@ -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},
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 74c26d8..eab85c0 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -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;
}