The Bitwise Blunder: How a Single Typo in Firefox’s Engine Opened the Door to RCE
A critical Remote Code Execution (RCE) vulnerability has been unearthed within SpiderMonkey, the JavaScript engine powering Mozilla Firefox. The provenance of this defect is almost farcical: a single-character typographical error within the WebAssembly garbage collection logic, where a developer substituted the bitwise AND operator (&) for the bitwise OR operator (|).
The flaw resided in the implementation of WebAssembly garbage collection and emerged following a refactoring of WebAssembly array metadata. The vulnerability was introduced via commit fcc2f20e35ec on January 19, 2026, within the source file js/src/wasm/WasmGcObject.cpp. The erroneous line of code was authored as:
Whereas the intended logic necessitated:
The distinction between & and | in this context is fundamental. Utilizing | 1 sets the least significant bit (LSB) to 1, effectively “tagging” the value. Conversely, & 1 isolates only the LSB while zeroing out all remaining bits. Due to memory alignment constraints, object addresses are typically multiples of 8 or 16, meaning their LSB is invariably 0. Consequently, the expression uintptr_t(oolHeaderNew) & 1 almost always yielded 0. Instead of a “tagged pointer” being recorded in the header field, the system inscribed a null value.
This typographical oversight compromised the WasmArrayObject::obj_moved() function, which is invoked when the garbage collector relocates WebAssembly arrays. For “out-of-line” (OOL) arrays, data is stored in a buffer separate from the object wrapper. Upon relocation, the superseded buffer must receive a forwarding pointer to the new data address. This pointer is distinguished from a standard header by a simple artifice: setting the LSB to 1. This allows the Ion JIT compiler—SpiderMonkey’s optimizing tier—to expeditiously recognize the value as a redirection rather than a conventional header.
Owing to the null value inscribed by the forwarding pointer, the logic for recognizing array formats collapsed. In isDataInline(), the validation is structured as:
A value with an LSB of 0 is interpreted as an “inline” (IL) variant, where data is considered embedded. A zero value satisfied this condition perfectly. Consequently, a relocated OOL array was erroneously designated as IL, causing the garbage collector and the JIT compiler to diverge in their interpretation of data localization. Notably, this vulnerability manifested exclusively in WebAssembly functions optimized by Ion; the Baseline compiler does not employ this specific stack frame update mechanism, thus remaining unaffected.
The vulnerability was discovered by a researcher known as Erge, who was scrutinizing the Firefox 149 Nightly source code in search of inspiration for a Capture The Flag (CTF) challenge. He successfully escalated this logical discrepancy into code execution within the Firefox renderer process. Erge constructed a proof-of-concept that transformed this logical error into a comprehensive system compromise through the following sequence:
-
Minor Garbage Collection: Triggered to force array relocation, resulting in a null value being written to the old buffer header.
-
Type Confusion: Within
wasm::Instance::updateFrameForMovingGC, the null value caused the array to be misidentified as inline. -
Stale Pointer: The function returned the superseded address rather than the updated one, leaving stack frames pointing to vacated memory.
-
Use-After-Free (UAF): Ion continued to operate on the freed memory region.
-
Heap Spraying: The researcher populated the heap with controlled values to occupy the vacated blocks.
-
Arbitrary R/W: Control over the OOL array base enabled arbitrary memory read and write primitives.
-
ASLR Bypass: An object spray containing relative pointers allowed the restoration of binary offsets.
-
Control Flow Hijack: The vtable was overwritten to redirect execution (hijacking the RIP register) to system commands, ultimately spawning a shell via
/bin/sh.
The timeline of the incident was remarkably brief. The defect was introduced on January 19, 2026. An independent researcher reported the flaw to the Mozilla bug tracker as Bug 2013739 around February 3, 2026. Erge submitted his findings as Bug 2014014 within the same 72-hour window. A corrective patch was committed on February 9, 2026, and the bug bounty was divided between the two researchers on February 11. Crucially, as the vulnerability was confined to the Nightly branch and never reached the stable release, the window for widespread exploitation remained firmly shut.
Support Our Threat Intelligence
If you find our technology report and cybersecurity news helpful, consider supporting our work.