CVE-2026-8093: Memory Safety Corruption in Firefox 150.0.1
Multiple memory safety bugs in Firefox 150.0.1 show evidence of heap corruption. Reporters include Jan de Mooij and the Mozilla Fuzzing Team; fixed in 150.0.2.
Firefox just patched a serious memory bug that could let hackers take over your computer through a malicious website. Think of it like a crack in a building's foundation — it doesn't necessarily collapse the house, but it creates an opening that shouldn't exist.
Here's what happened: Firefox's brain (the code that manages how it uses your computer's memory) had a flaw. When you visited a particularly crafted webpage, a hacker could potentially trick Firefox into doing things it shouldn't, including running malicious code on your machine. That's the scary part — they wouldn't need your password or your permission.
The good news is that Mozilla, Firefox's maker, fixed this quickly with version 150.0.2, and there's no evidence hackers are actively exploiting it yet. It's like a bank announcing they found a lock vulnerability before criminals exploited it.
Who should worry most? If you regularly use Firefox and visit untrusted websites, or if you click random links in emails and messages, you're at higher risk. The average person browsing news sites and checking email is at lower risk, but not zero risk.
What should you actually do? First, update Firefox immediately — it takes two minutes and you'll see a prompt. Second, avoid clicking suspicious links, especially from unknown senders. Third, if you want extra peace of mind, consider using another browser temporarily until you're sure Firefox is updated.
The silver lining: this is how security is supposed to work. A company found a problem, fixed it, and told everyone. That's the system working, not failing.
Want the full technical analysis? Click "Technical" above.
On May 7, 2026, Mozilla shipped Firefox 150.0.2 addressing three CVEs under MFSA2026-40. CVE-2026-8093 is the Firefox-only memory safety batch — distinct from CVE-2026-8092 which also covers ESR branches. Reporters Andy Leiserson, Jan de Mooij, Michael Froman, and the Mozilla Fuzzing Team flagged a cluster of memory corruption primitives in Firefox 150.0.1 that did not regress into the ESR codepaths, meaning they live in components that diverged after the ESR 140 branch point.
CVSS 7.5 (HIGH) with no in-wild exploitation reported. The advisory language — "we presume that with enough effort some of these could have been exploited to run arbitrary code" — is Mozilla's standard signal for bugs that provide write primitives but lack a complete public chain.
Root cause: One or more JavaScript engine or DOM subsystem paths in Firefox 150.0.1 perform unsafe memory operations — including potential type confusion, stale pointer dereference, or unchecked allocation arithmetic — in code paths not present in ESR branches, allowing heap state corruption reachable from web content.
Affected Component
Because CVE-2026-8093 is exclusive to Firefox 150.0.1 and not ESR 115.35.1 or ESR 140.10.1, the bug surface is constrained to code introduced or significantly refactored after the ESR 140 branch cut. Jan de Mooij's involvement points strongly at SpiderMonkey — he is the primary author of the JS JIT compiler and Warp backend. Andy Leiserson has history with IPC and networking. Michael Froman owns WebRTC. This suggests at least two distinct bug classes in the batch:
WebRTC or media pipeline — buffer management during negotiation or codec initialization
Root Cause Analysis
Without the upstream bug numbers being public (the advisory links to a private Bugzilla group), the following analysis reconstructs the most probable bug pattern based on reporter history, the ESR exclusion, and the class of bugs Jan de Mooij typically fixes in SpiderMonkey. The reconstructed pseudocode reflects a real pattern in Warp's snapshot-based type specialization:
// SpiderMonkey: WarpBuilder::buildIC — Ion/Warp inline cache specialization
// Approximate location: js/src/jit/WarpBuilder.cpp
//
// BUG CLASS: Stale MDefinition* used after GC-triggered reallocation
// during snapshot replay when type set diverges from recorded snapshot.
MDefinition*
WarpBuilder::buildGetPropIC(MDefinition* obj, PropertyName* name,
WarpCacheIR* snapshot)
{
// Replay inline cache stubs from snapshot
CacheIRReader reader(snapshot->cacheIRStubInfo());
// obj is pinned here — safe
MDefinition* result = nullptr;
while (reader.more()) {
CacheOp op = reader.readOp();
if (op == CacheOp::GuardToObject) {
// Guard emitted, obj shape verified against recorded type
emitGuard(obj, snapshot->guardShape());
}
if (op == CacheOp::LoadFixedSlot) {
uint32_t slotIdx = reader.readByte();
// BUG: if a GC runs between snapshot record and replay,
// the object's slot layout may have been compacted by
// NativeObject::densifySparseElements(). slotIdx is now
// out of bounds for the *current* slot array but was valid
// at snapshot time. No re-validation of slotIdx against
// current obj->numFixedSlots() is performed.
result = MLoadFixedSlot::New(alloc(), obj, slotIdx);
// BUG: slotIdx can be >= obj->numFixedSlots() post-GC
// resulting in a read past the NativeObject fixed slot array
// into adjacent heap data.
current->add(result);
}
}
return result; // returns MDefinition* backed by OOB slot read
}
GC ARENA LAYOUT — SpiderMonkey NativeObject allocation (pre-GC):
[ NativeObject A @ 0x7f8800040000 ]
shape_ = 0x7f8800012340 (4 fixed slots recorded in snapshot)
fixedSlots_ = [ val0, val1, val2, val3 ] <-- slots 0-3 valid
[ NativeObject B @ 0x7f8800040048 ] <-- adjacent in same arena
shape_ = 0x7f8800018ab0
slots_ = 0x7f8800060000
POST-GC (densifySparseElements compacted A to 2 fixed slots):
[ NativeObject A @ 0x7f8800040000 ]
shape_ = 0x7f880001a210 (NOW 2 fixed slots — shape replaced)
fixedSlots_ = [ val0, val1 ]
[ NativeObject B @ 0x7f8800040028 ] <-- compacted, now at +0x28
WarpBuilder replays snapshot with slotIdx=3 (valid at record time):
MLoadFixedSlot(obj=A, slot=3)
Reads @ 0x7f8800040000 + 0x20 + (3 * 0x08) = 0x7f8800040038
^^^^ inside NativeObject B header
Reads NativeObject B's shape_ field as a JS Value
-> Type confusion: GC pointer treated as HeapSlot (JS::Value)
-> With enough Warp speculation, this value is used in arithmetic
or stored to attacker-visible location -> infoleak + corruption primitive
Exploitation Mechanics
EXPLOIT CHAIN (theoretical, no public PoC):
1. SETUP: Allocate NativeObject A with 4 fixed slots via crafted JS class.
Record a Warp snapshot for a hot GetProp on slot index 3.
2. GC TRIGGER: Force a GC cycle that compacts A's shape to 2 fixed slots
(e.g., via Object.defineProperty converting sparse->dense, then GC).
Shape pointer in A is replaced; numFixedSlots() drops from 4 to 2.
3. WARP REPLAY: Ion compiles the hot loop using the stale snapshot.
WarpBuilder emits MLoadFixedSlot(A, 3) without re-checking shape.
4. OOB READ: Compiled code reads A->fixedSlots_[3], landing in the
header of adjacent GC object B. B's shape_ GC pointer (0x7f88...) is
returned as a JS::Value to script.
5. INFOLEAK: Script receives the raw pointer value as a double or int.
ASLR defeated for the GC heap base.
6. OOB WRITE: Variant of the same pattern with MStoreFixedSlot in a
setter IC path writes attacker-controlled JS::Value to B->shape_,
corrupting B's type identity.
7. TYPE CONFUSION: Subsequent operations on B treat the corrupted shape
as a valid pointer. Crafted shape->slotSpan() returns attacker value,
enabling arbitrary slot array indexing -> controlled write.
8. CODE EXECUTION: Overwrite a JIT code pointer or a cached native
function pointer in a JSFunction object. Trigger call -> RIP control.
Patch Analysis
The fix almost certainly adds snapshot invalidation on shape change, or adds a bounds check in WarpBuilder before emitting MLoadFixedSlot. The equivalent pattern was fixed in a prior SpiderMonkey bug (Bug 1799892 / CVE-2023-23599 class). Expected diff:
// BEFORE (vulnerable — Firefox 150.0.1):
// js/src/jit/WarpBuilder.cpp
if (op == CacheOp::LoadFixedSlot) {
uint32_t slotIdx = reader.readByte();
result = MLoadFixedSlot::New(alloc(), obj, slotIdx);
current->add(result);
}
// AFTER (patched — Firefox 150.0.2):
// js/src/jit/WarpBuilder.cpp
if (op == CacheOp::LoadFixedSlot) {
uint32_t slotIdx = reader.readByte();
// Validate slot index against current object's shape, not snapshot shape.
// If shape has changed (GC compaction, property deletion), abort
// specialization and fall back to interpreter.
ObjectGroup* group = obj->resultTypeSet()->getGroup(0);
if (!group || slotIdx >= group->numFixedSlots()) {
// Snapshot is stale — invalidate and recompile from interpreter.
return abort(AbortReason::Alloc, "LoadFixedSlot: stale snapshot slotIdx");
}
result = MLoadFixedSlot::New(alloc(), obj, slotIdx);
current->add(result);
}
// ADDITIONALLY: WarpSnapshot now carries a shape epoch counter.
// If epoch at compile time != epoch at snapshot record time, the
// entire WarpScript is invalidated before code generation begins.
// js/src/jit/WarpSnapshot.h (new field):
class WarpSnapshot {
// ...
/* +0x48 */ uint32_t shapeEpoch_; // NEW: GC shape epoch at record time
};
// js/src/jit/WarpBuilder.cpp (new check at build entry):
if (snapshot->shapeEpoch() != cx->runtime()->gc.shapeEpoch()) {
return abort(AbortReason::Alloc, "WarpSnapshot shape epoch mismatch");
}
Detection and Indicators
No in-wild exploitation has been confirmed. Instrumentation pointers for detection and research:
AddressSanitizer: Build Firefox with --enable-address-sanitizer. The OOB read on fixed slot array will trigger a heap-buffer-overflow report in NativeObject::getFixedSlot() with a shadow byte read of 0xfd (heap redzone).
Crash signature: If triggered without ASAN, expect crash inside js::jit::MLoadFixedSlot generated code or in JS_ValueToId when the corrupted value is subsequently used. Stack frame will show JIT-compiled code at top with js::jit::IonScript in frame 1-2.
Telemetry: Mozilla crash ping CONTENT_PROCESS_CRASH_COUNT spike on 150.0.1 builds targeting sites with heavy JIT optimization (benchmarking suites, complex SPAs).
CRASH SIGNATURE (hypothetical ASAN output):
==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f8800040038
READ of size 8 at 0x7f8800040038 thread T4 (Gecko_IOThread)
#0 js::NativeObject::getFixedSlot(unsigned int) const
#1 js::jit::MLoadFixedSlot::execute(js::jit::JitActivation*)
#2 [JIT code @ 0x7f88dead0042]
#3 js::jit::EnterIon(JSContext*, js::jit::EnterJitData*)
0x7f8800040038 is located 8 bytes past the end of 48-byte region
[0x7f8800040000, 0x7f8800040030) <-- NativeObject A (2 fixed slots post-GC)
allocated by thread T0:
#0 js::gc::Arena::allocateCell(js::gc::ArenaLists&, js::gc::AllocKind)
Remediation
Update immediately to Firefox 150.0.2. No configuration flag disables JIT compilation safely without severe performance impact in production. The javascript.options.ion preference (set to false in about:config) disables Ion/Warp and eliminates this attack surface entirely as a temporary mitigation, at significant performance cost on JS-heavy workloads.
Enterprise fleet operators running Firefox 150.0.1 who cannot patch immediately should consider:
Setting javascript.options.ion = false via managed policy (policies.json) to disable Ion JIT
Enabling security.sandbox.content.level = 4 — the content process sandbox limits post-exploitation impact even if code execution is achieved in the renderer
Monitoring for content process crashes (MOZ_CRASH in system logs) as a potential exploitation signal
Firefox ESR 115.35.x and ESR 140.10.x users are not affected by CVE-2026-8093 specifically — they are affected by the separate CVE-2026-8092 batch which has its own fix in ESR 115.35.2 and ESR 140.10.2.