Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionPrivilege escalation in the Data Loss Prevention component
ComponentDOM
Bug ClassLogic Error
Tracker2052565
Fix commitcb447ed89ad8 (firefox) +15/-1
CISA KEVNot listed
CreditedBrian Carpenter
Disclosed2026-07-21

Background

`ContentParent`
the parent-process (chrome-privileged) endpoint of the IPC channel that services requests from a single content (child) process in Gecko’s multi-process architecture.
`WindowGlobalParent`
the parent-process representation of a specific window/document, whose GetContentParent() identifies the content process that actually hosts that document.
Data Loss Prevention (DLP)
a Firefox enterprise policy layer that inspects clipboard operations against the requesting WindowContext to allow or block sensitive data flows.
`NS_ERROR_ILLEGAL_VALUE`
the error code the fix introduces to signal that a clipboard request named a WindowContext not owned by the calling process.

Root Cause Analysis

ContentParent::GetClipboardDataInternal accepted an aRequestingWindowContext argument supplied over IPC by a content process and used it—via its WindowGlobalParent—as the trusted context for the clipboard read and any associated DLP evaluation. The code never verified that the named window actually belonged to the calling content process, so the invariant “a process may only act on window contexts it hosts” was violated. A compromised child could therefore pass a WindowContext loaded in a different, more-privileged process to make its clipboard access appear to originate there, side-stepping the DLP checks keyed to that context.

The fix resolves the canonical WindowGlobalParent up front and rejects the request with NS_ERROR_ILLEGAL_VALUE when window->GetContentParent() != this, and the RecvGetClipboard / RecvGetClipboardDataIfSmallerThan handlers convert that error into an IPC_FAIL. This works because ownership is now enforced before any clipboard or policy logic runs, so a process can only reference its own windows.

Key insight
The single mistake was trusting the content process’s claimed aRequestingWindowContext without confirming the process owns that window; the fix adds an ownership check (window->GetContentParent() != this) that fails the IPC before the clipboard is read.

Attack Path

  1. Compromise a content process An attacker gains code execution in a low-privilege content process (e.g., via a separate renderer bug).
  2. Forge the requesting context The process sends a RecvGetClipboard / RecvGetClipboardDataIfSmallerThan message referencing a WindowContext that is actually loaded in a different, DLP-relevant process.
  3. Bypass ownership assumption GetClipboardDataInternal accepts the foreign WindowGlobalParent without checking GetContentParent(), treating it as the trusted origin of the request.
  4. Evade DLP evaluation The clipboard read and its policy check are attributed to the impersonated context, so DLP rules that should apply to the caller are not enforced.
  5. Exfiltrate clipboard data The attacker obtains clipboard contents it should have been prevented from reading.

Impact Assessment

An attacker who already controls a content process gains a privilege escalation that lets clipboard reads bypass the Data Loss Prevention policy layer by impersonating a WindowContext in another process. The gain is realized in the parent process’s clipboard IPC path, where the forged context is trusted. The precondition is prior compromise of, or code execution within, a content process able to send the clipboard IPC messages.

Changed Functions

FunctionChangeNotes
if
dom/ipc/ContentParent.cpp
modified

Files Changed

  • dom/ipc/ContentParent.cpp

Audit Directions

  • IPC-supplied context ownership
    Audit every parent-process handler that accepts a WindowContext, BrowsingContext, or WindowGlobalParent from a child and confirm it verifies GetContentParent() == this before trusting it.
  • Policy checks keyed to attacker-chosen identity
    Review DLP, permission, and security decisions that are attributed to a caller-provided context, ensuring the context cannot be one the caller does not own.
  • Consistent error-to-IPC_FAIL mapping
    Check that ownership/validation errors returned by internal helpers are actually surfaced as IPC_FAIL in every Recv* entry point rather than being silently ignored on some paths.
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index a35f7e4e082..3538da8e512 100644
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3343,6 +3343,11 @@ nsresult ContentParent::GetClipboardDataInternal(
     return NS_ERROR_INVALID_ARG;
   }
 
+  RefPtr<WindowGlobalParent> window = aRequestingWindowContext.get_canonical();
+  if (window && window->GetContentParent() != this) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
   if (NS_FAILED(rv)) {
@@ -3357,7 +3362,6 @@ nsresult ContentParent::GetClipboardDataInternal(
   }
 
   nsCOMPtr<nsITransferable> transferable = result.unwrap();
-  RefPtr<WindowGlobalParent> window = aRequestingWindowContext.get_canonical();
 
   rv = aFunction(clipboard, transferable, aWhichClipboard, window);
   if (NS_FAILED(rv)) {
@@ -3390,6 +3394,11 @@ mozilla::ipc::IPCResult ContentParent::RecvGetClipboard(
     return IPC_FAIL(this, "passed null window to RecvGetClipboard()");
   }
 
+  if (rv == NS_ERROR_ILLEGAL_VALUE) {
+    return IPC_FAIL(
+        this, "attempt to paste into WindowContext loaded in another process");
+  }
+
   return IPC_OK();
 }
 
@@ -3413,6 +3422,11 @@ mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataIfSmallerThan(
         this, "passed null window to RecvGetClipboardDataIfSmallerThan()");
   }
 
+  if (rv == NS_ERROR_ILLEGAL_VALUE) {
+    return IPC_FAIL(
+        this, "attempt to paste into WindowContext loaded in another process");
+  }
+
   aResolver(std::move(result));
   return IPC_OK();
 }
Loading diff…