Full Report
Browsers need to be fast - I mean, really fast. So, running JavaScript isn't always fast enough. Modern browsers perform Just-in-Time (JIT) compilations of JavaScript to native code, making it faster. This introduces an interesting yet incredibly complicated set of vulnerabilities to consider. This post is a Firefox JIT bug in the Pwn2Own competition. The Ion JIT compiler uses a function called ExtractLinearSum to convert a value into a linear sum expression. For instance, (x+(2+3)) - (-3) can be transformed into x+8. This type contains three parameters: This type contains three parameters: Value node MathSpace - an enum with the three values Modulo, Infinite and Unknown that will wrap around the integer space, bail if wrapping is needed and the final one is a default value. Recursive counter for stack depth exhaustion issues The function ExtractLinearSum is used multiple places in the Ion compiler, one of which is folding or simplifying the linear expressions. The function TryEliminateBoundsCheck is trying to merge bounds checks on the same object to simplify things. For instance, array[i+4]; array[i+7] will generate two bounds checks. To do this, it will create a bounds check object that can keep track of what's going on, eventually leading to a value of 7 being checked on the length. Although the usage of the MathSpace is useful, it's not rigorously verified. In the case of bounds checks, this seems pretty important! Module makes sense in some math cases but doesn't make sense in the case of bounds checks - infinite does. So, what if we can find a way to make the numbers being used in this operation of type Modulo on a bounds check? The following code triggers the bug when i is slightly less than 2^32: array[(i+5)|0]; array[(i+10)|0]. The |0 is used to force this to be 32 bits. The check will overflow because of the MathSpace being set to Modulo, leading to a faulty bounds check. This is only possible with really large arrays, requiring typed arrays to be practically feasible. Getting the write to happen in the proper location only requires fiddling with the minimum and maximum sizes in funky ways to trick the minimum/maximum counting for the bounds. To make this useful to exploit for an OOB read or OOB write, a useful object must be found in the huge address space. They found that Map objects were nice for getting a addrOf and fakeObj primitive. Once there, exploitation is trivial. It appears that this bug was found via manual source code review. Even though JavaScript engines are heavily fuzzed and reviewed, there are still great bugs lurking in unusual places. Overall, great write-up for somebody who knows nothing about browser engines!
Analysis Summary
# Vulnerability: Improper MathSpace Handling in Firefox Ion JIT (CVE-2025-4919)
## CVE Details
- **CVE ID**: CVE-2025-4919
- **CVSS Score**: Not explicitly listed in text, but categorized as high-impact (Pwn2Own Winner)
- **CWE**: CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer / CWE-190: Integer Overflow or Wraparound
## Affected Systems
- **Products**: Mozilla Firefox, SpiderMonkey JavaScript Engine
- **Versions**: Versions prior to 138.0.4
- **Configurations**: Systems running JavaScript with the IonMonkey JIT compiler enabled (default in desktop Firefox).
## Vulnerability Description
The vulnerability exists within the `ExtractLinearSum` function of the **Ion JIT compiler**. This function simplifies linear expressions (e.g., transforming `(x+5)-2` into `x+3`) and utilizes a parameter called `MathSpace`. There are two primary spaces: `Infinite` (which bails out on overflow) and `Modulo` (which allows 32-bit wrapping).
The flaw occurs because the `TryEliminateBoundsCheck` function, which merges multiple array bounds checks to optimize performance, fails to strictly verify the `MathSpace`. By forcing a `Modulo` space—often via bitwise operations like `|0` on large index values—an attacker can trigger an integer overflow during the bounds-check simplification. This leads the compiler to incorrectly calculate the range of valid indices, allowing the JIT-optimized code to bypass security checks for negative or out-of-bounds (OOB) indices.
## Exploitation
- **Status**: Exploited in the wild (demonstrated at Pwn2Own Berlin 2025).
- **Complexity**: High (Requires deep knowledge of JIT internals and heap grooming).
- **Attack Vector**: Network (Remote via malicious website/JavaScript).
## Impact
- **Confidentiality**: High (OOB Read allows leaking memory addresses and sensitive data).
- **Integrity**: High (OOB Write allows for memory corruption and control flow hijacking).
- **Availability**: High (Can result in application crashes).
## Remediation
### Patches
- **Mozilla Firefox 138.0.4**: This version contains the fix for CVE-2025-4919 (referenced in Security Advisory 2025-36).
### Workarounds
- **Disable JIT**: In high-security environments, `javascript.options.ion` can be set to `false` in `about:config`, though this significantly degrades performance.
- **Enhanced Sandboxing**: Ensure browser sandboxing remains enabled to prevent the renderer compromise from leading to a full system escape.
## Detection
- **Indicators of Compromise**: Presence of highly specific JavaScript patterns involving large TypedArrays (near 2^32 size) and intensive bitwise operations (`|0`) designed to trigger JIT compilation.
- **Detection Methods**: Signatures focusing on the specific "Map object" exploit technique used to gain `addrOf` and `fakeObj` primitives in SpiderMonkey.
## References
- **Mozilla Security Advisory**: hxxps[://]www[.]mozilla[.]org/en-US/security/advisories/mfsa2025-36/
- **Zero Day Initiative Advisory**: hxxps[://]www[.]zerodayinitiative[.]com/advisories/ZDI-25-291/
- **Vulnerability Source**: hxxps[://]www[.]zerodayinitiative[.]com/blog/2025/7/15/cve-2025-4919-corruption-via-math-space-in-mozilla-firefox