]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] GH-93252: Fix error handling for failed Python calls (GH-94693) (GH-94708)
authorKumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Sat, 9 Jul 2022 12:09:15 +0000 (17:39 +0530)
committerGitHub <noreply@github.com>
Sat, 9 Jul 2022 12:09:15 +0000 (05:09 -0700)
Automerge-Triggered-By: GH:tiran
Lib/test/test_call.py
Misc/NEWS.d/next/Core and Builtins/2022-07-08-11-44-45.gh-issue-93252.i2358c.rst [new file with mode: 0644]
Python/ceval.c

index 6936f093e3db1912e648a7eb00a544db8a1c3287..07355e8fa0616c09380fe824d85f9aab404ca02a 100644 (file)
@@ -26,6 +26,18 @@ class FunctionCalls(unittest.TestCase):
         self.assertIsInstance(res, dict)
         self.assertEqual(list(res.items()), expected)
 
+    def test_frames_are_popped_after_failed_calls(self):
+        # GH-93252: stuff blows up if we don't pop the new frame after
+        # recovering from failed calls:
+        def f():
+            pass
+        for _ in range(1000):
+            try:
+                f(None)
+            except TypeError:
+                pass
+        # BOOM!
+
 
 @cpython_only
 class CFunctionCallsErrorMessages(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-07-08-11-44-45.gh-issue-93252.i2358c.rst b/Misc/NEWS.d/next/Core and Builtins/2022-07-08-11-44-45.gh-issue-93252.i2358c.rst
new file mode 100644 (file)
index 0000000..1cc2d85
--- /dev/null
@@ -0,0 +1,2 @@
+Fix an issue that caused internal frames to outlive failed Python function
+calls, possibly resulting in memory leaks or hard interpreter crashes.
index 1a5454570518ff0baee4e1bbcc43958823edc181..c69ea21c149930a4d5531f67142cd871dca2b078 100644 (file)
@@ -6370,7 +6370,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
     }
     if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) {
         assert(frame->owner != FRAME_OWNED_BY_GENERATOR);
-        _PyFrame_Clear(frame);
+        _PyEvalFrameClearAndPop(tstate, frame);
         return NULL;
     }
     return frame;
@@ -6392,6 +6392,10 @@ fail:
 static void
 _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
 {
+    // Make sure that this is, indeed, the top frame. We can't check this in
+    // _PyThreadState_PopFrame, since f_code is already cleared at that point:
+    assert((PyObject **)frame + frame->f_code->co_nlocalsplus +
+        frame->f_code->co_stacksize + FRAME_SPECIALS_SIZE == tstate->datastack_top);
     tstate->recursion_remaining--;
     assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
     assert(frame->owner == FRAME_OWNED_BY_THREAD);