diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 6936f093e3db19..07355e8fa0616c 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -26,6 +26,18 @@ def fn(**kw): 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 00000000000000..1cc2d8560e53e5 --- /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 1a5454570518ff..c69ea21c149930 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 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, 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);
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: