Skip to content

Commit de0e2ee

Browse files
committed
Fix issue with _CREATE_INIT_FRAME
Fix a bug in `_CREATE_INIT_FRAME` where the frame is pushed to the stack on failure. `_CREATE_INIT_FRAME` pushes a pointer to the new frame onto the stack for consumption by the next uop. When pushing the frame fails, we do not want to push the result (NULL) to the stack because it is not a valid stackref and will be exposed to the generic error handling code in the interpreter loop. This worked in default builds because `PyStackRef_NULL` is `NULL` in default builds, which is not the case in free-threaded builds.
1 parent 3e8d85e commit de0e2ee

File tree

4 files changed

+33
-12
lines changed

4 files changed

+33
-12
lines changed

Lib/test/test_opcache.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,11 @@ def __init__(self):
500500
pass
501501

502502

503+
class InitTakesArg:
504+
def __init__(self, arg):
505+
self.arg = arg
506+
507+
503508
class TestCallCache(TestBase):
504509
def test_too_many_defaults_0(self):
505510
def f():
@@ -547,6 +552,20 @@ def count_args(self, *args):
547552
MyClass.__init__.__code__ = count_args.__code__
548553
instantiate()
549554

555+
@disabling_optimizer
556+
@requires_specialization_ft
557+
def test_push_init_frame_fails(self):
558+
def instantiate():
559+
return InitTakesArg()
560+
561+
for _ in range(2):
562+
with self.assertRaises(TypeError):
563+
instantiate()
564+
self.assert_specialized(instantiate, "CALL_ALLOC_AND_ENTER_INIT")
565+
566+
with self.assertRaises(TypeError):
567+
instantiate()
568+
550569

551570
@threading_helper.requires_working_threading()
552571
class TestRacesDoNotCrash(TestBase):

Python/bytecodes.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3744,13 +3744,14 @@ dummy_func(
37443744
shim->localsplus[0] = PyStackRef_DUP(self[0]);
37453745
DEAD(init);
37463746
DEAD(self);
3747-
init_frame = _PyEvalFramePushAndInit(
3747+
_PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
37483748
tstate, init[0], NULL, args-1, oparg+1, NULL, shim);
37493749
SYNC_SP();
3750-
if (init_frame == NULL) {
3750+
if (temp == NULL) {
37513751
_PyEval_FrameClearAndPop(tstate, shim);
37523752
ERROR_NO_POP();
37533753
}
3754+
init_frame = temp;
37543755
frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL;
37553756
/* Account for pushing the extra frame.
37563757
* We don't check recursion depth here,

Python/executor_cases.c.h

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 4 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)