← All issues

[7] [JSC] Delay PC advancement until after operationCallMayThrow in IPInt

Severity: Medium | Component: JSC IPInt (In-Place Interpreter) | 714bf5b

Rated Medium because the observable effect is wrong-catch selection in Wasm exception dispatch (a deterministic logic error reachable from web content), and escalation to Wasm-level type confusion depends on whether IPInt's catch-entry path rebuilds operand-stack height — an unverified architectural question.

Source/JavaScriptCore/llint/InPlaceInterpreter64.asm

ipintOp(_call_indirect, macro()
saveCallSiteIndex()
 
- loadb IPInt::CallIndirectMetadata::length[MC], t2
- advancePCByReg(t2)
-
move sp, a2
move cfr, a1
move MC, a3
- advanceMC(IPInt::CallIndirectMetadata::signature)
 
operationCallMayThrow(macro() cCall4(_ipint_extern_prepare_call_indirect) end)
 
+ # operationCallMayThrow saves the call site index, so we have to advance the PC after.
+ loadb IPInt::CallIndirectMetadata::length[MC], t3
+ advancePCByReg(t3)
+ advanceMC(IPInt::CallIndirectMetadata::signature)

Three IPInt opcode handlers — _call_indirect, _return_call_indirect, _return_call_ref — have their advancePCByReg and advanceMC instructions moved from before the operationCallMayThrow invocation to after it. No guards or new types are introduced; the order change is the entire fix.

Off-by-one in the PC value snapshotted as the call-site index for exception dispatch — PC advanced before the may-throw call records its location.

IPInt is JSC's lowest-tier WebAssembly interpreter, written in offlineasm (InPlaceInterpreter64.asm). Opcode handlers manipulate two pointers — PC (Wasm bytecode) and MC (metadata cursor) — and call C++ helpers via operationCallMayThrow for operations that may unwind. operationCallMayThrow records the current call site index — derived from PC at the moment of the call — into the per-frame state used by Wasm's exception-handler lookup. Wasm exception handling lets a function attach catch blocks to PC ranges; on throw, the runtime selects the innermost matching catch.

Pre-fix the three indirect-call handlers advanced PC past the call instruction before invoking operationCallMayThrow, so the saved call site index pointed at the instruction after the call. When the indirect call subsequently threw — via signature mismatch, null table entry, or a callee throw — the unwinder used this off-by-one index to scan the function's exception-handler table.

🔒

The exception-dispatch correctness implications and the open question of whether wrong-catch selection can be escalated into a stack-type primitive are explored in depth.

Subscribe to read more

🔒

Multiple reusable audit patterns for interpreter PC-snapshot ordering and Wasm handler-table lookup integrity, with concrete starting files and grep targets.

Subscribe to read more