mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
GH-93252: Fix error handling for failed Python calls (GH-94693)
This commit is contained in:
parent
4bed0db7c2
commit
8a285df806
3 changed files with 19 additions and 1 deletions
|
@ -26,6 +26,18 @@ class FunctionCalls(unittest.TestCase):
|
||||||
self.assertIsInstance(res, dict)
|
self.assertIsInstance(res, dict)
|
||||||
self.assertEqual(list(res.items()), expected)
|
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
|
@cpython_only
|
||||||
class CFunctionCallsErrorMessages(unittest.TestCase):
|
class CFunctionCallsErrorMessages(unittest.TestCase):
|
||||||
|
|
|
@ -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.
|
|
@ -6410,7 +6410,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
|
||||||
}
|
}
|
||||||
if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) {
|
if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) {
|
||||||
assert(frame->owner != FRAME_OWNED_BY_GENERATOR);
|
assert(frame->owner != FRAME_OWNED_BY_GENERATOR);
|
||||||
_PyFrame_Clear(frame);
|
_PyEvalFrameClearAndPop(tstate, frame);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return frame;
|
return frame;
|
||||||
|
@ -6432,6 +6432,10 @@ fail:
|
||||||
static void
|
static void
|
||||||
_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
|
_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_framesize ==
|
||||||
|
tstate->datastack_top);
|
||||||
tstate->recursion_remaining--;
|
tstate->recursion_remaining--;
|
||||||
assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
|
assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
|
||||||
assert(frame->owner == FRAME_OWNED_BY_THREAD);
|
assert(frame->owner == FRAME_OWNED_BY_THREAD);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue