High firefox UAF 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionSandbox escape due to use-after-free in the DOM: Navigation component
ComponentCore
Bug ClassUAF
Tracker2045468
Fix commit90ab644c07fe (firefox) +2/-2
CISA KEVNot listed
CreditedYaqoub Aldurayhim
Disclosed2026-07-21

Background

`BrowsingContext`
Gecko’s cross-process representation of a navigable browsing context (a window, tab, or frame) whose state is mirrored between the parent process and content processes.
Synced field
a BrowsingContext member (identified by a FieldIndex<> such as IDX_BrowserId) whose value is replicated across processes via IPC and gated by CanSet / DidSet hooks.
`CanSet`
the parent-process validation hook that decides whether a proposed change to a synced field, optionally originating from a content process (ContentParent* aSource), is permitted.
`BrowserId`
the identifier associating a top-level BrowsingContext with its owning browser element, used to correlate navigation state across processes.

Root Cause Analysis

The IDX_BrowserId synced field is stored as a 64-bit value, but the BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, ...) validation overload declared its aValue parameter as const uint32_t& instead of const uint64_t&. Because the parameter type did not match the field’s real width, the value flowing through the field-sync machinery was reinterpreted or truncated to 32 bits at the validation boundary, so CanSet did not reason about the same value the field actually carried. This breaks the invariant that a field’s validator inspects the exact value being committed, allowing a mismatch between what the parent validates and what is stored/propagated for the BrowserId. Given the metadata (a use-after-free in DOM navigation leading to sandbox escape), a BrowserId that is validated inconsistently or collides after truncation can misassociate a BrowsingContext, leaving navigation code holding a reference tied to a stale or wrong browser.

The fix changes both the declaration in BrowsingContext.h and the definition in BrowsingContext.cpp to take const uint64_t& aValue, so validation operates on the full-width identifier. With matched types there is no truncation and the parent enforces its “only settable on a top-level context that has no ID yet” check against the true value.

Key insight
The single core mistake was a width mismatch: a 64-bit synced field (IDX_BrowserId) validated through a CanSet overload typed as uint32_t, so the parent process gated a truncated value rather than the real one. Widening the parameter to uint64_t realigns the validator with the field, restoring correct enforcement over the identifier that binds a BrowsingContext to its browser.

Attack Path

  1. Reach the field sync A compromised or malicious content process issues an IPC request to set the BrowserId synced field on a BrowsingContext.
  2. Cross the type boundary The proposed 64-bit value is funneled through the mistyped CanSet(FieldIndex<IDX_BrowserId>, const uint32_t&, ...) overload, where it is narrowed to 32 bits at validation.
  3. Defeat consistent validation Because the parent validates a truncated value, its BrowserId gating no longer matches the value that governs BrowsingContext association.
  4. Misassociate navigation state Inconsistent BrowserId handling lets a BrowsingContext be tied to the wrong or a stale browser during navigation.
  5. Trigger the use-after-free Navigation code follows the misassociated linkage to a freed object, yielding the use-after-free described in the metadata.

Impact Assessment

An attacker who already controls a content process can, per the metadata, drive a use-after-free in the DOM navigation component and escalate to a sandbox escape. The corrupted validation occurs at the parent-process boundary for the BrowserId synced field, so the consequence crosses the content/parent trust boundary. The precondition is code execution or scripting control within a content process capable of proposing BrowsingContext field changes over IPC.

Files Changed

  • docshell/base/BrowsingContext.cpp
  • docshell/base/BrowsingContext.h

Audit Directions

  • Synced-field type parity
    Audit every CanSet and DidSet overload against its field’s declared storage type to ensure no uint32_t/uint64_t (or other width) mismatch narrows values at the validation boundary.
  • Identifier truncation
    Review parent-process validators that gate cross-process identifiers (BrowserId, context IDs, principal keys) for any implicit narrowing that could cause collisions or inconsistent enforcement.
  • IPC validation-vs-commit consistency
    Confirm that the value a parent-process hook validates is byte-for-byte the value subsequently stored and propagated, so a content process cannot make the checked value differ from the committed one.
diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp
index 4a8af95b0c1..0fcb739a037 100644
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -4234,7 +4234,7 @@ bool BrowsingContext::CanSet(
   return XRE_IsParentProcess() && !aSource && IsTop();
 }
 
-bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
+bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint64_t& aValue,
                              ContentParent* aSource) {
   // We should only be able to set this for toplevel contexts which don't have
   // an ID yet.
diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h
index e30efc99abb..7c50af51999 100644
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -1485,7 +1485,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
 
   void DidSet(FieldIndex<IDX_HasSessionHistory>, bool aOldValue);
 
-  bool CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
+  bool CanSet(FieldIndex<IDX_BrowserId>, const uint64_t& aValue,
               ContentParent* aSource);
 
   bool CanSet(FieldIndex<IDX_UseErrorPages>, const bool& aUseErrorPages,
Loading diff…