Medium CVSS 8.8 firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionA bug in WebAssembly code generation could have lead to a crash. It may have been possible for an attacker to leverage this to achieve code execution.
ComponentSpiderMonkey
Bug ClassLogic Error
Tracker1936454
Fix commite575212ce11b (firefox) +0/-1
CWECWE-94
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedNan Wang
Disclosed2025-02-04

Background

MIR
SpiderMonkey’s mid-level intermediate representation, in which every JIT/wasm operation is a node whose optimizer-visible attributes are declared in MIROps.yaml.
`movable`
an MIR-op attribute asserting the instruction is side-effect-free and may be freely reordered, hoisted, or deduplicated by the optimizer.
`GVN`/`LICM`
Global Value Numbering and Loop-Invariant Code Motion, passes that reuse or hoist any instruction flagged movable and matched by congruent_to.
`alias_set: none`
a declaration that the op reads no aliasable memory, so the optimizer assumes it can never observe an intervening store.

Root Cause Analysis

The vulnerable code path was the widening WebAssembly GC field-load MIR op (a WasmLoad*KA-style node carrying wideningOp: wasm::FieldWideningOp and result_type: Int32) as declared in MIROps.yaml. That op was marked movable: true while also declaring alias_set: none and congruent_to: if_operands_equal, which told the optimizer the load was a pure, memory-independent computation. In reality the op reads a packed (i8/i16) field out of a GC-managed struct or array, so its result depends on program order relative to stores into that field and on the point at which the containing object is valid. Because the load was treated as pure, GVN could common two such loads that straddle a store, and LICM could hoist the load out of a loop or above the store that defines its value — a code-generation logic error that yields a stale or incorrect widened value.

The fix removes the single movable: true line, so the load is no longer eligible for that motion or deduplication and stays pinned to its original data- and control-flow position.

Key insight
The core mistake was declaring a memory-reading wasm field load as movable (side-effect-free) even though its value depends on surrounding stores and object validity; deleting movable: true forces the optimizer to leave the load where it was emitted rather than hoisting or CSE-ing it into an unsound position.

Attack Path

  1. Craft a WebAssembly GC module Define a struct or array with a packed i8/i16 field and a hot function that stores to and then re-reads that field inside a loop.
  2. Reach the optimizing tier Execute the function enough times that it is Ion/wasm-optimized, so GVN and LICM process the movable widening load.
  3. Provoke the mis-optimization The load, treated as pure, is hoisted out of the loop or common-subexpression-eliminated with an earlier load, so it consumes a stale or wrong value rather than the current field contents.
  4. Corrupt program state The incorrect widened value flows into an index, length, or pointer computation, producing an out-of-bounds access or type confusion on the GC heap.
  5. Escalate to code execution The attacker shapes the resulting corruption into a controlled read/write primitive within the content process.

Impact Assessment

An attacker gains an incorrect-codegen bug that reliably crashes and may be leveraged into memory corruption and code execution inside the content process that runs the wasm. Exploitation requires only that the victim load attacker-controlled content that delivers a WebAssembly GC module and drives the function into the JIT’s optimizing tier; no elevated privileges are needed, though escaping the sandbox would require a further step.

Files Changed

  • js/src/jit/MIROps.yaml

Audit Directions

  • Purity vs. memory
    Review every MIR op that pairs alias_set: none with movable: true yet actually reads heap or GC memory, since that combination falsely declares the load side-effect-free.
  • Wasm GC accessors
    Audit the widening/packed field and element load ops (wasm::FieldWideningOp users) for correct alias_set and movability, especially where a mutable field can be stored to between loads.
  • congruent_to overreach
    Check memory-reading ops using congruent_to: if_operands_equal, which can be CSE-merged across intervening stores when the alias/movable metadata understates their dependencies.
diff --git a/js/src/jit/MIROps.yaml b/js/src/jit/MIROps.yaml
index e450b90788d..7d175b8601b 100644
--- a/js/src/jit/MIROps.yaml
+++ b/js/src/jit/MIROps.yaml
@@ -736,7 +736,6 @@
     wideningOp: wasm::FieldWideningOp
   type_policy: none
   result_type: Int32
-  movable: true
   congruent_to: if_operands_equal
   alias_set: none
 
Loading diff…