Skip to content

Commit 305cfe8

Browse files
brandtbuchertiran
authored andcommitted
[3.11] pythonGH-93252: Fix error handling for failed Python calls (pythonGH-94693)
(cherry picked from commit 8a285df) Co-authored-by: Brandt Bucher <brandtbucher@microsoft.com>
1 parent b4e232c commit 305cfe8

File tree

3 files changed

+19
-1
lines changed

3 files changed

+19
-1
lines changed

Lib/test/test_call.py

+12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ def fn(**kw):
2626
self.assertIsInstance(res, dict)
2727
self.assertEqual(list(res.items()), expected)
2828

29+
def test_frames_are_popped_after_failed_calls(self):
30+
# GH-93252: stuff blows up if we don't pop the new frame after
31+
# recovering from failed calls:
32+
def f():
33+
pass
34+
for _ in range(1000):
35+
try:
36+
f(None)
37+
except TypeError:
38+
pass
39+
# BOOM!
40+
2941

3042
@cpython_only
3143
class CFunctionCallsErrorMessages(unittest.TestCase):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix an issue that caused internal frames to outlive failed Python function
2+
calls, possibly resulting in memory leaks or hard interpreter crashes.

Python/ceval.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -6370,7 +6370,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
63706370
}
63716371
if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) {
63726372
assert(frame->owner != FRAME_OWNED_BY_GENERATOR);
6373-
_PyFrame_Clear(frame);
6373+
_PyEvalFrameClearAndPop(tstate, frame);
63746374
return NULL;
63756375
}
63766376
return frame;
@@ -6392,6 +6392,10 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
63926392
static void
63936393
_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
63946394
{
6395+
// Make sure that this is, indeed, the top frame. We can't check this in
6396+
// _PyThreadState_PopFrame, since f_code is already cleared at that point:
6397+
assert((PyObject **)frame + frame->f_code->co_framesize ==
6398+
tstate->datastack_top);
63956399
tstate->recursion_remaining--;
63966400
assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
63976401
assert(frame->owner == FRAME_OWNED_BY_THREAD);

0 commit comments

Comments
 (0)