Critical firefox Logic Error 🔧 Commit mapped

Overview

Critical
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactcritical
DescriptionWe are aware that exploit code for this is public however we are not aware of any attacks in the wild abusing this flaw.
ComponentCore
Bug ClassLogic Error
Tracker2043820
Fix commit04e4ed871c2c (firefox) +6/-0
CISA KEVNot listed
CreditedAtsushi Sada
Disclosed2026-07-14

Background

`nsDocShellLoadState`
the object that packages up all parameters of a navigation (target mURI, mTriggeringPrincipal, mTriggeringRemoteType, flags) and is passed across processes to drive a docshell load.
`javascript:` scheme
a URI scheme whose “load” executes script in the context of the target document rather than fetching a resource.
`mTriggeringRemoteType`
the identifier of the process that initiated the load (NOT_REMOTE_TYPE meaning the trusted parent process, any other value meaning a specific content child process).
IPC deserialization constructor
the nsDocShellLoadState constructor variant that takes an aActor and rebuilds the load state from data sent by a child process, so its fields are attacker-influenced.

Root Cause Analysis

The vulnerable path is the nsDocShellLoadState::nsDocShellLoadState constructor that reconstructs a load request from IPC data supplied by a content process via aActor.

Before the fix, this constructor validated the triggering principal but never checked the combination of a javascript: target URI with a triggering remote type belonging to a content process, so a javascript: load could be attributed to and execute across a process boundary. The violated invariant is that a javascript: “navigation” must remain intra-process — it must run only in the same content process that legitimately triggered it — because it executes script directly against the target document.

The fix adds an explicit guard: if mURI->SchemeIs("javascript") and mTriggeringRemoteType != NOT_REMOTE_TYPE, the parent treats the message as malformed, calls aActor->FatalError, and aborts. This works because a genuine same-process javascript: load never needs to cross a process boundary, so rejecting cross-process javascript: loads outright closes the injection channel without breaking legitimate flows.

Key insight
The core mistake was trusting a content-process-supplied load request to attribute a javascript: execution across processes without verifying that such a scheme should never carry a foreign mTriggeringRemoteType. The fix rejects any javascript: load whose triggering remote type is not NOT_REMOTE_TYPE, restoring the intra-process invariant for script-executing schemes.

Attack Path

  1. Compromise a content process The attacker gains code execution in a sandboxed content process (for example via a separate renderer bug).
  2. Forge a load request From that process, craft an IPC message that constructs an nsDocShellLoadState with a javascript: mURI and a mTriggeringRemoteType naming a different process/context.
  3. Bypass the missing check The parent’s deserialization constructor accepts the request because it only validated the principal, not the scheme-versus-remote-type combination.
  4. Cross-process script injection The javascript: load is executed in a target document belonging to a different origin or process, running attacker-controlled script there.
  5. Escalate The injected script operates with the target document’s privileges, defeating same-origin and process isolation boundaries.

Impact Assessment

An attacker who already controls one content process gains the ability to execute javascript: in another origin’s or process’s document context, a universal cross-site-scripting / process-isolation bypass driven from the privileged parent’s load logic. The critical precondition is prior code execution or message-forging capability in a content process; the flaw itself lives in the trusted parent process that arbitrates cross-process loads. This turns a single compromised renderer into cross-origin script injection, which is why the impact is rated critical and public exploit code exists.

Files Changed

  • docshell/base/nsDocShellLoadState.cpp

Audit Directions

  • Scheme-sensitive IPC validation
    Audit every parent-side deserialization constructor and IPC handler that consumes a child-supplied URI for schemes with side effects (javascript:, data:, blob:) and confirm the scheme is checked against the triggering process, not just the principal.
  • Remote-type invariants
    Search for uses of mTriggeringRemoteType and GetEffectiveTriggeringRemoteType and verify that NOT_REMOTE_TYPE versus content-process values is enforced wherever a load can execute script rather than fetch a resource.
  • Cross-process load state trust
    Review all fields of nsDocShellLoadState reconstructed from IPC for combinations that are only valid intra-process, ensuring the parent rejects rather than silently honors attacker-influenced cross-process attributions.
diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp
index 957c4db74f1..ea31776de66 100644
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -162,6 +162,12 @@ nsDocShellLoadState::nsDocShellLoadState(
       return;
     }
 
+    if (mURI->SchemeIs("javascript") &&
+        mTriggeringRemoteType != NOT_REMOTE_TYPE) {
+      aActor->FatalError("Illegal cross-process javascript: load attempt");
+      return;
+    }
+
     if (!ValidatePrincipalCouldPotentiallyBeLoadedBy(
             mTriggeringPrincipal, GetEffectiveTriggeringRemoteType(),
             {ValidatePrincipalOptions::AllowExpanded,
Loading diff…