]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-94262: Don't create frame objects for frames that aren't yet complete. (GH-94371)
authorMark Shannon <mark@hotpy.org>
Fri, 1 Jul 2022 10:08:20 +0000 (11:08 +0100)
committerGitHub <noreply@github.com>
Fri, 1 Jul 2022 10:08:20 +0000 (11:08 +0100)
Include/internal/pycore_frame.h
Lib/test/test_generators.py
Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst [new file with mode: 0644]
Objects/codeobject.c
Objects/frameobject.c
Python/frame.c
Python/sysmodule.c

index eed26fbb06218a3efc365b5c361a6c53c01e14bf..994c205c3d1644c6c555bf36d67352b88b742b65 100644 (file)
@@ -134,6 +134,21 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame, PyObject **stack_pointer)
     frame->stacktop = (int)(stack_pointer - frame->localsplus);
 }
 
+/* Determine whether a frame is incomplete.
+ * A frame is incomplete if it is part way through
+ * creating cell objects or a generator or coroutine.
+ *
+ * Frames on the frame stack are incomplete until the
+ * first RESUME instruction.
+ * Frames owned by a generator are always complete.
+ */
+static inline bool
+_PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
+{
+    return frame->owner != FRAME_OWNED_BY_GENERATOR &&
+    frame->prev_instr < _PyCode_CODE(frame->f_code) + frame->f_code->_co_firsttraceable;
+}
+
 /* For use by _PyFrame_GetFrameObject
   Do not call directly. */
 PyFrameObject *
@@ -145,6 +160,8 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame);
 static inline PyFrameObject *
 _PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
 {
+
+    assert(!_PyFrame_IsIncomplete(frame));
     PyFrameObject *res =  frame->frame_obj;
     if (res != NULL) {
         return res;
index 87a7dd69d106c45a5ef08d4ac3578641d2bfb852..e5aa7da1e0df13b8a666759cca944bf2082e08f4 100644 (file)
@@ -170,6 +170,43 @@ class GeneratorTest(unittest.TestCase):
             g.send(0)
         self.assertEqual(next(g), 1)
 
+    def test_handle_frame_object_in_creation(self):
+
+        #Attempt to expose partially constructed frames
+        #See https://github.com/python/cpython/issues/94262
+
+        def cb(*args):
+            inspect.stack()
+
+        def gen():
+            yield 1
+
+        thresholds = gc.get_threshold()
+
+        gc.callbacks.append(cb)
+        gc.set_threshold(1, 0, 0)
+        try:
+            gen()
+        finally:
+            gc.set_threshold(*thresholds)
+            gc.callbacks.pop()
+
+        class Sneaky:
+            def __del__(self):
+                inspect.stack()
+
+        sneaky = Sneaky()
+        sneaky._s = Sneaky()
+        sneaky._s._s = sneaky
+
+        gc.set_threshold(1, 0, 0)
+        try:
+            del sneaky
+            gen()
+        finally:
+            gc.set_threshold(*thresholds)
+
+
 
 class ExceptionTest(unittest.TestCase):
     # Tests for the issue #23353: check that the currently handled exception
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst
new file mode 100644 (file)
index 0000000..7ba39bb
--- /dev/null
@@ -0,0 +1,3 @@
+Don't create frame objects for incomplete frames. Prevents the creation of
+generators and closures from being observable to Python and C extensions,
+restoring the behavior of 3.10 and earlier.
index 6f2a837240384662093663f99c2eeec9af638ce9..d4fa0e38aed629adc7ad3c2012308afc75c63da1 100644 (file)
@@ -638,11 +638,10 @@ PyCode_New(int argcount, int kwonlyargcount,
                                      exceptiontable);
 }
 
-static const char assert0[4] = {
-    LOAD_ASSERTION_ERROR,
-    0,
-    RAISE_VARARGS,
-    1
+static const char assert0[6] = {
+    RESUME, 0,
+    LOAD_ASSERTION_ERROR, 0,
+    RAISE_VARARGS, 1
 };
 
 PyCodeObject *
@@ -666,7 +665,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
     if (filename_ob == NULL) {
         goto failed;
     }
-    code_ob = PyBytes_FromStringAndSize(assert0, 4);
+    code_ob = PyBytes_FromStringAndSize(assert0, 6);
     if (code_ob == NULL) {
         goto failed;
     }
index 9ff0443e4558f4e8e9e74a0ec20a77020127f939..44cc06205a623775dda375f69612642e561a3354 100644 (file)
@@ -1169,8 +1169,14 @@ PyFrame_GetBack(PyFrameObject *frame)
 {
     assert(frame != NULL);
     PyFrameObject *back = frame->f_back;
-    if (back == NULL && frame->f_frame->previous != NULL) {
-        back = _PyFrame_GetFrameObject(frame->f_frame->previous);
+    if (back == NULL) {
+        _PyInterpreterFrame *prev = frame->f_frame->previous;
+        while (prev && _PyFrame_IsIncomplete(prev)) {
+            prev = prev->previous;
+        }
+        if (prev) {
+            back = _PyFrame_GetFrameObject(prev);
+        }
     }
     Py_XINCREF(back);
     return back;
index 206ecab44c1b871ae2090ffe14738409864233b9..7c6705e208f3d37a1db04a1affc874e0f6f903f2 100644 (file)
@@ -68,9 +68,13 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
     f->f_frame = frame;
     frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
     assert(f->f_back == NULL);
-    if (frame->previous != NULL) {
+    _PyInterpreterFrame *prev = frame->previous;
+    while (prev && _PyFrame_IsIncomplete(prev)) {
+        prev = prev->previous;
+    }
+    if (prev) {
         /* Link PyFrameObjects.f_back and remove link through _PyInterpreterFrame.previous */
-        PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous);
+        PyFrameObject *back = _PyFrame_GetFrameObject(prev);
         if (back == NULL) {
             /* Memory error here. */
             assert(PyErr_ExceptionMatches(PyExc_MemoryError));
index 444042f82d9473d2a52abcb520dfc4cda093dfa7..93120071b400fd5ee61a32b5796bc7fb703cedf5 100644 (file)
@@ -1776,9 +1776,17 @@ sys__getframe_impl(PyObject *module, int depth)
         return NULL;
     }
 
-    while (depth > 0 && frame != NULL) {
-        frame = frame->previous;
-        --depth;
+    if (frame != NULL) {
+        while (depth > 0) {
+            frame = frame->previous;
+            if (frame == NULL) {
+                break;
+            }
+            if (_PyFrame_IsIncomplete(frame)) {
+                continue;
+            }
+            --depth;
+        }
     }
     if (frame == NULL) {
         _PyErr_SetString(tstate, PyExc_ValueError,