CVE-2026-12302
Overview
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 theWindowfor 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.
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
- 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.
- Invoke the constructor
From that context, script calls
new SharedWorker(...), reachingSharedWorker::Constructorwith the non-current window. - 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.
- Establish the worker connection
A
SharedWorkeris created and connected on behalf of the discarded document, exercising storage/worker machinery outside the active-document boundary.
Impact Assessment
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 checksAudit other worker, storage, and lifecycle-sensitive constructors and entry points that fetch an inner window via
do_QueryInterfacebut never callIsCurrentInnerWindowbefore acting. - Stale-global reentryLook 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 patternsFlag code that only
MOZ_ASSERT(window)and then proceeds, since assertions are compiled out in release and do not enforce liveness or currency invariants.
Patch
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