Skip to content

Commit 28178f4

Browse files
committed
W22: bounds-check nextInstr() in YIELD_VALUE pattern-deopt
Fixes regression in bffec89 where bci.nextInstr() was called on the final YIELD_VALUE bytecode without bounds-checking, producing a BytecodeInstruction that points past co_code. Reading .opcode() / .oparg() on that invalid instruction SIGSEGVs. Per Python/jit/bytecode.h:57 contract: nextInstrOffset() will go past the end of the instruction stream for the last instruction. Match the existing pattern at builder.cpp:1143 (BCOffset comparison against bc_block.size()) before dereferencing the next instruction. Catch credit: testkeeper 14:34:52Z controlled experiment showed auto-compile of asyncio internals (which contain YIELD_VALUE near end-of-bytecode) SIGSEGV'd at iter 1000 (auto-compile threshold), while pre-bffec89650 binary at the same tree exited cleanly. The dis.dis fixture verification at bffec89 commit-time only exercised small functions where YIELD_VALUE was never the last instruction — real production codepaths exposed the boundary case. Updates the author-attribution comment to use timestamp citations (per supervisor 14:30:40Z discipline correction post medic catch on shepard 14:29:14Z directive).
1 parent bffec89 commit 28178f4

1 file changed

Lines changed: 23 additions & 12 deletions

File tree

Python/jit/hir/builder.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4899,7 +4899,8 @@ void HIRBuilder::checkTranslate() {
48994899
banned_name_ids.insert(i);
49004900
}
49014901
}
4902-
for (auto& bci : BytecodeInstructionBlock{code_}) {
4902+
BytecodeInstructionBlock bc_block{code_};
4903+
for (auto& bci : bc_block) {
49034904
auto opcode = bci.opcode();
49044905
int oparg = bci.oparg();
49054906
if (!isSupportedOpcode(opcode)) {
@@ -4938,17 +4939,27 @@ void HIRBuilder::checkTranslate() {
49384939
// 2=after-yield-from, 3=after-await). Refcount-pass-init n_in==0 on
49394940
// the generator-resume bb crashes deopt with 'register not live'
49404941
// assertion (5 architectural fix iterations did not converge cleanly).
4941-
// Author authority bypass per Alex L3076 + gatekeeper L3076 + theologian
4942-
// + supervisor concur on (C) pattern-deopt scope. Plain yield (RESUME
4943-
// oparg==1) and await (RESUME oparg==3) STAY in the JIT — only the
4944-
// yield-from desugaring pattern falls back to the interpreter.
4945-
// RULE STANDS for future cases; this is the ONE explicit exception.
4946-
auto next_bc = bci.nextInstr();
4947-
if (next_bc.opcode() == RESUME && next_bc.oparg() == 2) {
4948-
throw std::runtime_error{fmt::format(
4949-
"Cannot compile {} to HIR because it uses 'yield from' "
4950-
"(W22 deopt: YIELD_VALUE+RESUME-oparg-2 pattern)",
4951-
preloader_.fullname())};
4942+
// Author authority bypass per Alex 14:00:40Z + gatekeeper 13:59:54Z +
4943+
// theologian 14:15:09Z + supervisor 14:14:50Z concur on (C) pattern-
4944+
// deopt scope. Plain yield (RESUME oparg==1) and await (RESUME
4945+
// oparg==3) STAY in the JIT — only the yield-from desugaring pattern
4946+
// falls back to the interpreter. RULE STANDS for future cases; this
4947+
// is the ONE explicit exception.
4948+
//
4949+
// Bounds check: nextInstrOffset() can go past end of bytecode for the
4950+
// last instruction (per Python/jit/bytecode.h:57 contract). Guard
4951+
// before reading the next instruction's opcode/oparg, otherwise
4952+
// YIELD_VALUE at end-of-bytecode SIGSEGVs reading garbage memory.
4953+
// (testkeeper 14:34:52Z auto-compile asyncio regression catch.)
4954+
BCOffset next_off = bci.nextInstrOffset();
4955+
if (next_off < bc_block.size()) {
4956+
auto next_bc = bci.nextInstr();
4957+
if (next_bc.opcode() == RESUME && next_bc.oparg() == 2) {
4958+
throw std::runtime_error{fmt::format(
4959+
"Cannot compile {} to HIR because it uses 'yield from' "
4960+
"(W22 deopt: YIELD_VALUE+RESUME-oparg-2 pattern)",
4961+
preloader_.fullname())};
4962+
}
49524963
}
49534964
}
49544965
}

0 commit comments

Comments
 (0)