Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionMitigation bypass in the DOM: Security component
ComponentDOM
Bug ClassLogic Error
Tracker2034489
Fix commitb924b91af262 (firefox) +6/-0
CISA KEVNot listed
Creditedlebr0nli
Disclosed2026-06-16

Background

`SharedWorker`
a Web Worker that can be accessed by multiple browsing contexts sharing the same origin, created through SharedWorker::Constructor.
Inner window
the Gecko object (nsPIDOMWindowInner) backing the Window for a specific loaded document, replaced when the document is navigated away or discarded.
`IsCurrentInnerWindow`
a predicate that returns whether an inner window is still the live, current one for its outer window rather than a stale or pending-discard document.
Mitigation bypass
an attacker action that circumvents an existing security boundary or lifecycle guard rather than corrupting memory directly.

Root Cause Analysis

SharedWorker::Constructor obtained the current inner window via do_QueryInterface and proceeded to create a shared worker without checking whether that window was still the live, current inner window for its browsing context. The vulnerable path allowed a document that was already on its way to being discarded (for example a window mid-navigation or otherwise no longer current) to spin up a SharedWorker, violating the invariant that worker-backed, storage-touching APIs should only be exercised by the currently active document. Because a stale document could still reach the constructor, its principal and lifecycle state could be used to establish a shared worker connection outside the intended lifecycle, bypassing the security expectations tied to the active document.

The fix adds an explicit if (!window->IsCurrentInnerWindow()) guard that throws an InvalidStateError and returns nullptr before any worker is created. This works because it rejects construction exactly when the window is no longer the authoritative document, closing the window during which a discarded-document context could act.

Key insight
The single mistake was constructing a SharedWorker from an inner window without first confirming it was still the current inner window, letting a soon-to-be-discarded document create a worker; the fix addresses this by gating construction behind window->IsCurrentInnerWindow() and throwing when the check fails.

Attack Path

  1. Obtain a stale window Script arranges for a document whose inner window is no longer current (e.g. a window that is navigating away or being discarded) while still holding a reference to its global.
  2. Invoke the constructor From that context, script calls new SharedWorker(...), reaching SharedWorker::Constructor with the non-current window.
  3. Bypass the lifecycle guard Without the check, the constructor proceeds past the point where a current-document invariant should have blocked it, using the stale document’s principal and state.
  4. Establish the worker connection A SharedWorker is created and connected on behalf of the discarded document, exercising storage/worker machinery outside the active-document boundary.

Impact Assessment

An attacker gains the ability to create a SharedWorker from a document that is no longer the current inner window, bypassing the lifecycle mitigation that should confine such APIs to the active document, within the content process and same-origin script context. The precondition is script execution in a page able to reach the constructor with a going-to-be-discarded window; the rated impact is moderate (medium) and is a mitigation bypass / logic error rather than direct memory corruption.

Files Changed

  • dom/workers/sharedworkers/SharedWorker.cpp

Audit Directions

  • Missing current-inner-window checks
    Audit other worker, storage, and lifecycle-sensitive constructors and entry points that fetch an inner window via do_QueryInterface but never call IsCurrentInnerWindow before acting.
  • Stale-global reentry
    Look for DOM APIs reachable during or after navigation/discard where a retained window or global reference can still drive privileged or storage-backed operations.
  • Guard-after-assert patterns
    Flag code that only MOZ_ASSERT(window) and then proceeds, since assertions are compiled out in release and do not enforce liveness or currency invariants.
diff --git a/dom/workers/sharedworkers/SharedWorker.cpp b/dom/workers/sharedworkers/SharedWorker.cpp
index 7593414e2ad..a74d9258b77 100644
--- a/dom/workers/sharedworkers/SharedWorker.cpp
+++ b/dom/workers/sharedworkers/SharedWorker.cpp
@@ -82,6 +82,12 @@ already_AddRefed<SharedWorker> SharedWorker::Constructor(
       do_QueryInterface(aGlobal.GetAsSupports());
   MOZ_ASSERT(window);
 
+  if (!window->IsCurrentInnerWindow()) {
+    aRv.ThrowInvalidStateError(
+        "Cannot create worker for a going to be discarded document");
+    return nullptr;
+  }
+
   // Our current idiom is that storage-related APIs specialize for the system
   // principal themselves, which is consistent with StorageAllowedForwindow not
   // specializing for the system principal.  Without this specialization we
Loading diff…