Pwn2Own Berlin: Critical IonMonkey JIT Bug Exposes Firefox to Memory Corruption
During the Pwn2Own Berlin 2025 competition, security researcher Manfred Paul successfully demonstrated an attack against the Mozilla Firefox browser’s rendering process by exploiting a vulnerability in the IonMonkey JIT compiler. Although he did not achieve a full sandbox escape from the JavaScript engine, the discovery itself was of significant importance. The flaw was assigned CVE-2025-4919, and Mozilla promptly addressed the issue the following day in Firefox version 138.0.4, as detailed in security bulletin 2025-36. The Zero Day Initiative cataloged the exploit under ZDI-25-291.
IonMonkey serves as the Just-In-Time compiler within SpiderMonkey—the JavaScript and WebAssembly engine underpinning Firefox. As part of its optimization routine, IonMonkey uses the ExtractLinearSum
function to analyze linear expressions, transforming constructs like “x + n” into simplified forms. This process is critical for optimizing operations such as array bounds checks.
The ExtractLinearSum
function returns a SimpleLinearSum
structure, which encapsulates a linear combination of a variable and a constant. It supports various arithmetic models: modular arithmetic (Modulo), unbounded arithmetic (Infinite), and a dynamically determined mode (Unknown), depending on context.
The vulnerability lies within the TryEliminateBoundsCheck
function, which is responsible for removing redundant array bounds validations. If the compiler encounters consecutive index checks that can be normalized into the same variable with differing constants—such as i+5
and i+10
—it merges them into one. However, under modular arithmetic, this leads to faulty calculations.
For example, when the bitwise OR operation |0
is used, expressions are forcibly coerced into 32-bit integers, inducing modular behavior. This misleads the compiler into treating indices like (i+5)|0
and (i+10)|0
as differing by a fixed offset, neglecting potential overflows.
As Paul demonstrated, this causes the compiler to erroneously consolidate bounds checks, permitting access to negative indices—effectively out-of-bounds. When applied to a Uint8Array
of size 2³² bytes, this vulnerability enables both reads and writes beyond the allocated memory, laying the groundwork for further exploitation.
A successful attack only requires crafting an array and an index variable whose value approaches the boundary of the 32-bit space. Due to quirks in the compiler’s optimization logic, critical safety checks may be eliminated under specific conditions, allowing an attacker to reference memory at a negative offset—thereby breaching the memory of other objects.
Firefox’s implementation nuances make it feasible to obtain so-called addrOf
and fakeObj
primitives, which enable attackers to retrieve the memory address of an object and craft counterfeit ones. These capabilities allow precise control over memory layout and manipulation of object structures.
The final phase of the attack involves injecting shellcode into an executable memory region—such as within a WASM function—and hijacking its entry point. This ultimately results in arbitrary code execution. A demonstration of the exploit is available on YouTube.
Such vulnerabilities remain exceedingly rare and are notoriously elusive for automated tools like fuzzers. The flaw described in CVE-2025-4919 arises from intricate compiler logic and requires large-scale memory allocations, reducing the likelihood of its discovery during conventional testing. This case underscores once again the critical importance of source-level auditing in uncovering severe software bugs.