High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the Storage: IndexedDB component
ComponentDOM
Bug ClassLogic Error
Tracker2053649
Fix commitde86c9876a0c (firefox) +43/-13
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-13

Changed Functions

FunctionChangeNotes
if
dom/indexedDB/ActorsChild.cpp
modified
switch
dom/indexedDB/IDBCursor.cpp
modified
if
dom/indexedDB/Key.cpp
modified

Files Changed

  • dom/indexedDB/ActorsChild.cpp
  • dom/indexedDB/IDBCursor.cpp
  • dom/indexedDB/IDBCursor.h
  • dom/indexedDB/Key.cpp
diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp
index b295a1147fd..bc1beb9445c 100644
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -2688,8 +2688,14 @@ void BackgroundCursorChild<CursorType>::SendContinueInternal(
 template <IDBCursorType CursorType>
 void BackgroundCursorChild<CursorType>::CompleteContinueRequestFromCache() {
   AssertIsOnOwningThread();
+
+  if (!mCursor) {
+    mStrongCursor = nullptr;
+    mDelayedResponses.clear();
+    return;
+  }
+
   MOZ_ASSERT(mTransaction);
-  MOZ_ASSERT(mCursor);
   MOZ_ASSERT(mStrongCursor);
   MOZ_ASSERT(!mDelayedResponses.empty());
   MOZ_ASSERT(mCursor->GetType() == CursorType);
@@ -2991,9 +2997,7 @@ void BackgroundCursorChild<CursorType>::ActorDestroy(ActorDestroyReason aWhy) {
 
   if (mCursor) {
     mCursor->ClearBackgroundActor();
-#ifdef DEBUG
     mCursor = nullptr;
-#endif
   }
 
 #ifdef DEBUG
diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp
index 3161e8df338..66281f09938 100644
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -161,6 +161,23 @@ bool IDBTypedCursor<CursorType>::IsSourceDeleted() const {
   return !sourceObjectStore || sourceObjectStore->IsDeleted();
 }
 
+template <IDBCursor::Type CursorType>
+bool IDBTypedCursor<CursorType>::CheckContinueState(ErrorResult& aRv) const {
+  AssertIsOnOwningThread();
+
+  if (!mTransaction->IsActive()) {
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
+    return false;
+  }
+
+  if (IsSourceDeleted() || !mHaveValue || mContinueCalled) {
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
+    return false;
+  }
+
+  return true;
+}
+
 void IDBCursor::ResetBase() {
   AssertIsOnOwningThread();
 
@@ -338,13 +355,7 @@ void IDBTypedCursor<CursorType>::Continue(JSContext* const aCx,
                                           ErrorResult& aRv) {
   AssertIsOnOwningThread();
 
-  if (!mTransaction->IsActive()) {
-    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
-    return;
-  }
-
-  if (IsSourceDeleted() || !mHaveValue || mContinueCalled) {
-    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
+  if (!CheckContinueState(aRv)) {
     return;
   }
 
@@ -356,6 +367,10 @@ void IDBTypedCursor<CursorType>::Continue(JSContext* const aCx,
     return;
   }
 
+  if (!CheckContinueState(aRv)) {
+    return;
+  }
+
   if constexpr (!IsObjectStoreCursor) {
     if (IsLocaleAware() && !key.IsUnset()) {
       auto result = key.ToLocaleAwareKey(GetSourceRef().Locale());
@@ -486,6 +501,10 @@ void IDBTypedCursor<CursorType>::ContinuePrimaryKey(
       return;
     }
 
+    if (!CheckContinueState(aRv)) {
+      return;
+    }
+
     const Key& sortKey = mData.GetSortKey(IsLocaleAware());
 
     switch (mDirection) {
diff --git a/dom/indexedDB/IDBCursor.h b/dom/indexedDB/IDBCursor.h
index 23379a2916a..b928c9a55ea 100644
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -243,6 +243,8 @@ class IDBTypedCursor : public IDBCursor {
 
   bool IsSourceDeleted() const;
 
+  bool CheckContinueState(ErrorResult& aRv) const;
+
  protected:
   virtual ~IDBTypedCursor() override;
 
diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp
index af0e9b51c8d..b3be5914345 100644
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -1017,11 +1017,16 @@ Key::SetFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal,
 
   const bool shouldInactivate = aTransaction && aTransaction->IsActive();
   if (shouldInactivate) {
-    aTransaction->TransitionToInactive();
+    aTransaction->TransitionToInactiveWithDeferral();
   }
   auto guard = MakeScopeExit([&]() {
-    if (shouldInactivate && !aTransaction->IsAborted()) {
-      aTransaction->TransitionToActive();
+    if (shouldInactivate) {
+      if (!aTransaction->IsAborted()) {
+        aTransaction->TransitionToActive();
+      } else {
+        aTransaction->DeactivateDeferral();
+        aTransaction->DrainDeferredResponses();
+      }
     }
   });
 
Loading diff…