High firefox Memory Corruption 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionMemory safety bugs present in Firefox 152. Some of these bugs showed evidence of memory corruption and we presume that with enough effort some of these could have been exploited to run arbitrary code.
ComponentDOM
Bug ClassMemory Corruption
Tracker2048936
Fix commitd7af90a2b719 (firefox) +7/-1
CISA KEVNot listed
CreditedC.M.Chang, Christian Holler, David Parks, Dennis Jackson, Gabriele Svelto, Hafiizh, Henrik Skupin, Jan de Mooij, Jens Stutte, Paul Theriault, Randell Jesup, Rob Wu, Tom Ritter, Tom Schuster and the Mozilla Fuzzing Team
Disclosed2026-07-21

Background

IndexedDB transaction
an atomic unit of database work in the DOM IndexedDB API, represented by IDBTransaction, that moves through lifecycle states as requests run.
`ReadyState`
the IDBTransaction lifecycle enum whose values (Inactive, Active, Finished) gate whether new requests may be placed against the transaction and whether post-abort safety guards apply.
`TransitionToActive`
the IDBTransaction method that drains deferred responses and then flips the transaction back to the Active state so queued work can proceed.
Deferred responses
request completions queued while the transaction was Inactive and replayed by DrainDeferredResponses, whose handlers can run author (page) script.

Root Cause Analysis

The vulnerable path was IDBTransaction::TransitionToActive, which called DrainDeferredResponses() and then unconditionally executed mReadyState = ReadyState::Active. Draining deferred responses can invoke JavaScript event handlers that abort or otherwise finish the transaction, driving mReadyState to Finished as a side effect. The invariant violated is that a transaction which has already been aborted/finished must never be resurrected to Active, because the post-abort guards that reject further operations rely on the state not being Active. By clobbering mReadyState back to Active after such a reentrant abort, the code reopened a finished transaction to further requests, defeating those guards and leading to memory corruption.

The fix asserts the state is Inactive or Finished and only sets Active when it is still Inactive, so a transaction finished during draining stays Finished.

Key insight
The single mistake was unconditionally restoring the Active state after DrainDeferredResponses could reentrantly run script and abort the transaction; the fix makes the transition conditional on the transaction still being Inactive, preserving the terminal Finished state and keeping post-abort guards effective.

Attack Path

  1. Open a transaction and queue work Author script starts an IDBTransaction and issues requests so that responses become deferred while the transaction is Inactive.
  2. Trigger draining The transaction re-activates, entering TransitionToActive and calling DrainDeferredResponses(), which replays deferred completions.
  3. Abort during a drained handler A completion handler runs attacker script that aborts (finishes) the transaction, setting mReadyState to Finished mid-drain.
  4. State clobbered back to Active Control returns to TransitionToActive, which unconditionally sets mReadyState = ReadyState::Active, resurrecting the finished transaction.
  5. Use freed/finished state Subsequent requests bypass post-abort guards and operate on a transaction whose backing state was already torn down, producing memory corruption.

Impact Assessment

An attacker who can run JavaScript on a page gains a memory-corruption primitive in the content process by driving an IDBTransaction through a reentrant abort during response draining. Exploitation requires no special privileges beyond loading attacker-controlled web content that scripts IndexedDB, and Mozilla presumes such memory corruption could, with effort, be leveraged toward arbitrary code execution.

Changed Functions

FunctionChangeNotes
if
dom/indexedDB/IDBTransaction.cpp
modified

Files Changed

  • dom/indexedDB/IDBTransaction.cpp

Audit Directions

  • Reentrancy after script-running calls
    Audit any code that reads or restores object state after a call (like DrainDeferredResponses) that can dispatch events or run author script, since the object may have been finished or freed during the call.
  • Unconditional state transitions
    Flag lifecycle transitions that write a state value unconditionally rather than guarding on the expected prior state, especially where a terminal state (Finished) must be sticky.
  • IndexedDB lifecycle guards
    Review other IDBTransaction and IndexedDB actor methods for post-abort guards that assume the transaction cannot re-enter Active, and verify no path re-activates a finished transaction.
diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp
index e5afac2e2e4..00216a36798 100644
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -345,7 +345,13 @@ void IDBTransaction::TransitionToActive() {
 
   DrainDeferredResponses();
 
-  mReadyState = ReadyState::Active;
+  // Draining can run script that aborts the transaction; only become
+  // Active again if we're still Inactive so post-abort guards stay effective.
+  MOZ_DIAGNOSTIC_ASSERT(mReadyState == ReadyState::Inactive ||
+                        mReadyState == ReadyState::Finished);
+  if (mReadyState == ReadyState::Inactive) {
+    mReadyState = ReadyState::Active;
+  }
 }
 
 void IDBTransaction::TransitionToInactiveWithDeferral() {
Loading diff…