CVE-2026-65334
Overview
Background
- Select specialization (ReduceStrength)
- A B3 transform that clones the values between a Select and a Check to specialize each Select arm.
- Cloning-forbidden value kind
- A B3 value whose identity must be unique (e.g. a call patchpoint tied to an exception stackmap); cloning it aliases semantics.
- Catch-restoration stackmap / CallSiteIndex
- Per-call-site exception state used to restore registers/stack when unwinding to a catch; aliasing it across call sites corrupts restoration.
Root Cause Analysis
This fixes a B3 miscompilation where strength reduction’s Select specialization cloned values that must not be cloned — specifically call patchpoints inside a try/catch that carry an exception-restoration stackmap. In B3ReduceStrength, when a Select feeds a Check, specializeSelect splits the path and CLONES the values between the Select and the Check to specialize each arm.
Pre-patch, it did this whenever a suitable Select was found, without verifying the intervening values are cloneable. Separately, WASM call patchpoints generated inside a try (OMGIRGenerator::createCallPatchpoint with m_tryCatchDepth) carry a catch-restoration stackmap keyed by CallSiteIndex (used by preparePatchpointForExceptions to restore state when an exception unwinds to the catch). Cloning such a patchpoint would create two call sites aliasing ONE stackmap/CallSiteIndex, so on exception the wrong state is restored — a type-confusion/memory-corruption condition.
The fix marks those in-try call patchpoints as cloningForbidden (auto patchpointKind = m_tryCatchDepth ? cloningForbidden(Patchpoint) : Patchpoint, with an ASSERT in preparePatchpointForExceptions that the patch kind is cloningForbidden), and makes specializeSelect check that ALL values between the Select and the Check are cloneable (kind().isCloningForbidden()) before specializing — bailing if any forbids cloning.
The restored invariant is that B3 never clones a value whose identity is semantically required (like an exception-stackmap-bearing call), so exception restoration cannot be aliased across call sites.
Attack Path
- Compile WASM with try/catch calls Run a WASM module with calls inside a try block so OMG generates call patchpoints carrying catch-restoration stackmaps.
- Feed a Select to a Check Shape the IR so B3 strength reduction attempts Select specialization over a region containing such a call patchpoint.
- Clone the uncloneable Pre-patch, specializeSelect clones the in-try call patchpoint, creating two call sites aliasing one exception stackmap/CallSiteIndex.
- Corrupt exception restoration On an exception unwinding to the catch, the aliased stackmap restores wrong state — a type-confusion/memory-corruption in the WebContent process.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ReduceStrength Select specialization (specializeSelect gate)Source/JavaScriptCore/b3/B3ReduceStrength.cpp |
modified | Before specializing, walks the values from m_index back to the Select and bails if any value->kind().isCloningForbidden(), so uncloneable values (e.g. in-try call patchpoints) are never cloned. |
OMGIRGenerator::createCallPatchpoint / preparePatchpointForExceptionsSource/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp |
modified | Marks call patchpoints created inside a try (m_tryCatchDepth) as cloningForbidden(Patchpoint) so a B3 transform cannot alias two call sites to one catch-restoration stackmap; asserts the kind is cloningForbidden. |
Files Changed
JSTests/wasm/stress/omg-reduce-strength-select-exception-stackmap.jsSource/JavaScriptCore/b3/B3ReduceStrength.cppSource/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp
Audit Directions
- Clone-safety of transformsAudit B3 transforms that clone values (Select specialization, tail duplication) for checks against kind().isCloningForbidden().
- Identity-bearing patchpointsGrep for patchpoints/values keyed by CallSiteIndex or exception stackmaps and confirm they are marked cloningForbidden.