Skip to content

Commit 8a285df

Browse files
authored
GH-93252: Fix error handling for failed Python calls (GH-94693)
1 parent 4bed0db commit 8a285df

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
@@ -6410,7 +6410,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
64106410
}
64116411
if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) {
64126412
assert(frame->owner != FRAME_OWNED_BY_GENERATOR);
6413-
_PyFrame_Clear(frame);
6413+
_PyEvalFrameClearAndPop(tstate, frame);
64146414
return NULL;
64156415
}
64166416
return frame;
@@ -6432,6 +6432,10 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
64326432
static void
64336433
_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
64346434
{
6435+
// Make sure that this is, indeed, the top frame. We can't check this in
6436+
// _PyThreadState_PopFrame, since f_code is already cleared at that point:
6437+
assert((PyObject **)frame + frame->f_code->co_framesize ==
6438+
tstate->datastack_top);
64356439
tstate->recursion_remaining--;
64366440
assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
64376441
assert(frame->owner == FRAME_OWNED_BY_THREAD);

0 commit comments

Comments
 (0)