summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKumar Aditya <59607654+kumaraditya303@users.noreply.github.com>2022-07-09 12:09:15 (GMT)
committerGitHub <noreply@github.com>2022-07-09 12:09:15 (GMT)
commit7a341724e4dc8188f01ac338deaa414c4e6542c4 (patch)
tree96bd2e096a12cbd81994f9a772a513495b57d71f
parentb4e232c4b5d977578b3c6aa86d8b76085167c313 (diff)
downloadcpython-7a341724e4dc8188f01ac338deaa414c4e6542c4.zip
cpython-7a341724e4dc8188f01ac338deaa414c4e6542c4.tar.gz
cpython-7a341724e4dc8188f01ac338deaa414c4e6542c4.tar.bz2
[3.11] GH-93252: Fix error handling for failed Python calls (GH-94693) (GH-94708)
Automerge-Triggered-By: GH:tiran
-rw-r--r--Lib/test/test_call.py12
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-07-08-11-44-45.gh-issue-93252.i2358c.rst2
-rw-r--r--Python/ceval.c6
3 files changed, 19 insertions, 1 deletions
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 6936f09..07355e8 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -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
index 0000000..1cc2d85
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-07-08-11-44-45.gh-issue-93252.i2358c.rst
@@ -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.
diff --git a/Python/ceval.c b/Python/ceval.c
index 1a54545..c69ea21 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -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);