CVE-2024-27851
Overview
Background
- OMG / B3IRGenerator
- JSC’s optimizing wasm tier and its B3 IR generator, which can inline direct calls between wasm functions.
- Call-site index
- An index identifying a call site used to look up exception-handling metadata during unwinding.
- hasExceptionHandlers
- Per-function state indicating whether a wasm function contains try/catch handlers, needed to emit correct EH metadata when inlined.
- CalleeGroup
- The per-module registry of compiled wasm callees, used here to look up an inlinee’s real exception-handler state.
Root Cause Analysis
When JavaScriptCore’s optimizing wasm compiler (OMG/B3) inlines a direct call, the inlinee’s B3IRGenerator must know whether the inlined function contains exception handlers, because that determines the call-site index metadata emitted for exception dispatch.
Pre-patch, the inlining constructor B3IRGenerator(parentCaller, rootCaller, functionIndex, returnContinuation, args) hardcoded m_hasExceptionHandlers(false) and never consulted the actual inlinee, and emitInlineDirectCall used a shortcut bool mayHaveExceptionHandlers = !m_hasExceptionHandlers || m_hasExceptionHandlers.value(); to decide whether to emit a real call-site index or PatchpointExceptionHandle::s_invalidCallSiteIndex. So when a function that does have exception handlers was inlined, the generator believed it had none and emitted invalid/placeholder call-site indices for the inlined region. If an exception was then thrown across that inlined frame, the unwinder resolved the wrong (invalid) call-site index and used incorrect exception-handling metadata — an out-of-bounds / type-confused read of handler tables that WebKit’s advisory describes as leading to arbitrary code execution.
The fix threads the module’s CalleeGroup& into the generators and, in emitInlineDirectCall, looks up the real inlinee (wasmEntrypointCalleeFromFunctionIndexSpace(...).hasExceptionHandlers()) to pass a correct std::optional<bool> hasExceptionHandlers into the inlinee generator; it also propagates the parent’s exception-handler state (if (parentCaller.m_hasExceptionHandlers && *parentCaller.m_hasExceptionHandlers) m_hasExceptionHandlers = { true }), removes the invalid-call-site-index shortcut (always emitting proper indices), and extends PatchpointExceptionHandle to carry a callSiteIndex.
The restored invariant is that inlined wasm frames always carry accurate has-exception-handlers state and valid call-site indices, so exception unwinding indexes the correct handler metadata.
Attack Path
- Craft a wasm module with EH + inlining Build a module where a small function containing exception handlers is a hot inlining target of a caller compiled by OMG/B3.
- Force optimizing compilation + inlining Warm the functions so OMG inlines the EH-bearing callee via emitInlineDirectCall, which pre-patch records the inlinee as having no exception handlers.
- Emit invalid call-site metadata The inlined region is compiled with placeholder/invalid call-site indices because m_hasExceptionHandlers was false.
- Throw across the inlined frame Trigger an exception in the inlined callee so the unwinder resolves the wrong call-site index and reads incorrect exception-handler metadata.
- Corrupt / hijack control flow The mismatched handler metadata yields an out-of-bounds/type-confused dispatch that the advisory rates as arbitrary code execution in WebContent.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
B3IRGenerator inlining constructorSource/JavaScriptCore/wasm/WasmB3IRGenerator.cpp |
modified | Takes CalleeGroup& and an explicit std::optional<bool> hasExceptionHandlers, initializes m_hasExceptionHandlers from it, and inherits the parent's true exception-handler state instead of hardcoding false. |
B3IRGenerator::emitInlineDirectCallSource/JavaScriptCore/wasm/WasmB3IRGenerator.cpp |
modified | Looks up the real inlinee via m_calleeGroup.wasmEntrypointCalleeFromFunctionIndexSpace(...).hasExceptionHandlers() and always emits valid call-site indices (removes the s_invalidCallSiteIndex shortcut). |
parseAndCompileB3Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp |
modified | Signature gains CalleeGroup& so the generator can resolve inlinee callees; OMGPlan/OSREntryPlan updated to pass m_calleeGroup. |
PatchpointExceptionHandleSource/JavaScriptCore/wasm/WasmIRGeneratorHelpers.h |
modified | Now carries a callSiteIndex alongside hasExceptionHandlers so exception patchpoints reference the correct site. |
Audit Directions
- Other inlining-propagated stateAudit B3IRGenerator’s inlining constructor for other fields defaulted/hardcoded rather than derived from the inlinee (stack checks, memory mode, tier-up).
- Call-site index emissiongrep wasm/ for s_invalidCallSiteIndex and mayHaveExceptionHandlers to find other places that substitute placeholder call-site indices based on possibly-wrong EH state.
- hasExceptionHandlers plumbingTrace hasExceptionHandlers/CalleeGroup::hasExceptionHandlers through OMG/OSR/BBQ compilation to ensure every inlined or tiered path uses the callee’s true value.