Medium watch CVSS 3.1 chrome Authorization Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
3.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in SiteIsolation
DescriptionMissing authorization in SiteIsolation
ComponentSiteIsolation
Bug ClassAuthorization Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker516665605
Fix commit03b18568967f (chromium/src) +53/-0
CWECWE-862
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-26 (per vendor advisory)
Fix landed2026-07-22 (public git)
Disclosed2026-08-25
Patch gap34 days — fix public in source before the advisory

Background

`RestrictedCookieManager`
a Mojo interface from the network service, bound per frame and per origin, through which a renderer reads and writes cookies (backing document.cookie and cookieStore).
PDF process isolation
PDF content is committed in a dedicated renderer process whose SiteInfo has is_pdf set. That process is meant to be cut off from cookies and storage of the origin that served the PDF.
`BrowserInterfaceBroker` fallback
besides the eager bind in RenderFrameHostImpl::SendCommitNavigation(), a frame can request RestrictedCookieManager later through its interface broker, which calls RenderFrameHostImpl::BindRestrictedCookieManager().
`ChildProcessSecurityPolicyImpl::CanAccessDataForOrigin`
the browser-side Site Isolation check that decides whether a given renderer process may access data (cookies, storage) belonging to an origin.

Root Cause Analysis

The browser enforced cookie isolation for PDF processes on only one of two bind paths. SendCommitNavigation() skipped eagerly binding a RestrictedCookieManager for PDF processes, but the broker-driven RenderFrameHostImpl::BindRestrictedCookieManager() had no such gate. It called BindRestrictedCookieManagerWithOrigin() unconditionally with the frame’s GetLastCommittedOrigin(). A frame committed in a PDF-isolated process could therefore request the interface through the broker and get a cookie manager scoped to the origin that served the PDF. That broke the invariant that PDF renderers never touch that origin’s cookies, and the regression test confirms that document.cookie could read and write such cookies before the fix.

The patch adds an authorization check at the top of BindRestrictedCookieManager(): if CanAccessDataForOrigin(process_id, GetLastCommittedOrigin()) fails, it returns early without binding. The check also covers sandboxed-frame processes. The receiver is dropped, so later cookie IPCs fail closed: reads see no cookies and writes do nothing. As the commit explains, the browser deliberately does not kill the renderer, because benign sandboxed frames can legitimately reach this path via cookieStore.get().

Key insight
The access-control decision for cookie access from PDF-isolated processes lived in only one of two bind paths. The fix moves an authoritative CanAccessDataForOrigin check into the broker bind handler itself, so every path to a RestrictedCookieManager is authorized by the browser.

Attack Path

  1. Get content into a PDF-isolated process a site serves content that commits as PDF, so the frame runs in a process whose SiteInfo has is_pdf set and which should have no cookie access for that origin.
  2. Request the cookie interface through the broker code running in that process (in the test, script calling document.cookie) asks its BrowserInterfaceBroker for a RestrictedCookieManager, instead of relying on the commit-time bind that was correctly withheld.
  3. Browser binds without authorization before the fix, BindRestrictedCookieManager() bound the interface for GetLastCommittedOrigin() with no CanAccessDataForOrigin check.
  4. Read or write origin cookies the PDF process could then read and set cookies of the origin that served the PDF. In practice this is most meaningful if the PDF renderer is compromised, for example through a PDF-engine bug (inferred).

Impact Assessment

The flaw is a browser-process authorization gap that undermines Site Isolation’s containment of PDF renderers. A PDF-isolated process could read or modify cookies, including session cookies, of the origin that served the PDF, defeating the defense-in-depth isolation that is meant to limit the damage from a compromised PDF renderer (realistic exploitation likely assumes such a compromise; inferred). It gives no code execution in itself, and the advisory states no in-the-wild exploitation.

Changed Functions

FunctionChangeNotes
IN_PROC_BROWSER_TEST_F
content/browser/renderer_host/cookie_browsertest.cc
modified

Files Changed

  • content/browser/renderer_host/cookie_browsertest.cc
  • content/browser/renderer_host/render_frame_host_impl.cc

Audit Directions

  • Eager vs. broker-bound interfaces
    for every interface that SendCommitNavigation() or commit params bind conditionally (storage, cache, permissions, file system), check that the matching BrowserInterfaceBroker binder in RenderFrameHostImpl applies the same restrictions for PDF, sandboxed or otherwise restricted processes.
  • Binders that trust `GetLastCommittedOrigin()` without `CanAccessDataForOrigin`
    look for browser-side binders that scope an origin-keyed data interface to the frame’s committed origin without confirming that the hosting process is allowed to access that origin’s data.
  • Process-type restrictions on data access
    audit how is_pdf, sandboxed-frame and other restricted SiteInfo flags feed into ChildProcessSecurityPolicyImpl. Every data-bearing interface (RestrictedCookieManager, storage keys, CacheStorage, IndexedDB) should consult that policy consistently.
From 03b18568967f29eb7d198a513c9be98291a5ce6c Mon Sep 17 00:00:00 2001
From: Alex Moshchuk <alexmos@chromium.org>
Date: Wed, 22 Jul 2026 13:23:28 -0700
Subject: [PATCH] Block PDF processes from binding RestrictedCookieManager

RenderFrameHostImpl::SendCommitNavigation() already skips the eager
RestrictedCookieManager bind [1] for PDF processes, since PDF renderers
are isolated from cookies and storage for the origin that served the
PDF. The BrowserInterfaceBroker fallback path,
RenderFrameHostImpl::BindRestrictedCookieManager(), did not have the
same gate, so a frame committed in a PDF-isolated process could still
obtain a RestrictedCookieManager for its committed origin via the
broker.

Add a CanAccessDataForOrigin check to BindRestrictedCookieManager() so
that we do not grant the cookie manager interface to PDF renderer
processes, and also to sandboxed frame processes as a bonus. There's
intentionally no renderer kill added, because the renderer doesn't
prevent a sandboxed frame process from requesting this interface via
cookieStore.get(). (The renderer does have subsequent security checks
[2] to deny access to cookies from opaque origins before it ever tries
to use that interface.)

Add a (mostly Gemini-written) content_browsertest that commits a page
with LoadURLParams::is_pdf set and verifies that document.cookie can
neither read nor write cookies for the committed origin. The new test
fails without the fix applied.

[1]
https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.cc;l=17120;drc=b251030f22d6810f6755262d2182316f60552d60

[2]
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/cookie_store/cookie_store.cc;l=506;drc=bd8f7f70a1197b77f4bb0a886e2778f707e333e1

Bug: 516665605
Change-Id: Id5f13ce3f10cec726c5736daffcadcfbdf67001f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8127316
Reviewed-by: Charlie Reis <creis@chromium.org>
Commit-Queue: Alex Moshchuk <alexmos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1666575}
---
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index f3e0c1f..ca28b2c3 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -15058,6 +15058,24 @@
 
 void RenderFrameHostImpl::BindRestrictedCookieManager(
     mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) {
+  // Check whether the current frame is permitted to access cookies. For
+  // example, this will avoid binding the interface for PDF or sandboxed frame
+  // processes that should never need to access cookies.
+  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+  if (!policy->CanAccessDataForOrigin(GetProcess()->GetID().GetUnsafeValue(),
+                                      GetLastCommittedOrigin())) {
+    // Note that there's intentionally no renderer kill here because the
+    // renderer doesn't prevent code in a sandboxed frame from requesting this
+    // interface via cookieStore.get() (despite such a frame having an opaque
+    // origin and no access to any cookies). The renderer does have subsequent
+    // security checks to deny access to cookies from opaque origins before it
+    // ever tries to use that interface, though. If a compromised renderer skips
+    // those checks and attempts to send IPCs to read/write cookies, those IPCs
+    // would fail if this interface isn't bound; writes would therefore be
+    // no-ops and reads would behave as if there are no cookies.
+    return;
+  }
+
   BindRestrictedCookieManagerWithOrigin(
       std::move(receiver), GetIsolationInfoForSubresources(),
       GetLastCommittedOrigin(), GetCookieSettingOverrides());
diff --git a/content/browser/renderer_host/cookie_browsertest.cc b/content/browser/renderer_host/cookie_browsertest.cc
index 015c8a1d..708a2836 100644
--- a/content/browser/renderer_host/cookie_browsertest.cc
+++ b/content/browser/renderer_host/cookie_browsertest.cc
@@ -22,6 +22,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/security_principal.h"
 #include "content/public/browser/site_isolation_policy.h"
@@ -832,6 +833,40 @@
       v.DepictFrameTree(tab->GetPrimaryFrameTree().root()));
 }
 
+// Verifies that a frame committed in a PDF-isolated process cannot bind a
+// RestrictedCookieManager for the committed origin. The SendCommitNavigation
+// path already skips the bind for PDF processes, so this exercises the
+// BrowserInterfaceBroker fallback used when no cookie manager was supplied at
+// commit time.
+IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CookiesBlockedForPdfProcess) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  WebContentsImpl* tab = static_cast<WebContentsImpl*>(shell()->web_contents());
+  GURL url = embedded_test_server()->GetURL("a.test", "/empty.html");
+
+  SetCookieDirect(tab, url, "A=1");
+  ASSERT_EQ("A=1", GetCookiesDirect(tab, url));
+
+  // Commit `url` as PDF content so that the resulting frame runs in a process
+  // whose SiteInfo has `is_pdf` set.
+  NavigationController::LoadURLParams params(url);
+  params.transition_type = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+  params.is_pdf = true;
+  NavigateToURLBlockUntilNavigationsComplete(
+      tab, params, 1, /*ignore_uncommitted_navigations=*/false);
+  ASSERT_TRUE(IsLastCommittedEntryOfPageType(tab, PAGE_TYPE_NORMAL));
+  ASSERT_EQ(url, tab->GetLastCommittedURL());
+
+  RenderFrameHost* frame = tab->GetPrimaryMainFrame();
+
+  // The PDF process must not be able to read or write cookies for the committed
+  // origin.
+  EXPECT_EQ("", GetCookieFromJS(frame));
+  std::ignore = EvalJs(frame, "document.cookie = 'B=2'");
+  EXPECT_EQ("A=1", GetCookiesDirect(tab, url));
+}
+
 IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CookieNotReadableAfterExpiry) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/renderer_host/cookie_browsertest.cc b/content/browser/renderer_host/cookie_browsertest.cc
index 015c8a1d..708a2836 100644
--- a/content/browser/renderer_host/cookie_browsertest.cc
+++ b/content/browser/renderer_host/cookie_browsertest.cc
@@ -22,6 +22,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/security_principal.h"
 #include "content/public/browser/site_isolation_policy.h"
@@ -832,6 +833,40 @@
       v.DepictFrameTree(tab->GetPrimaryFrameTree().root()));
 }
 
+// Verifies that a frame committed in a PDF-isolated process cannot bind a
+// RestrictedCookieManager for the committed origin. The SendCommitNavigation
+// path already skips the bind for PDF processes, so this exercises the
+// BrowserInterfaceBroker fallback used when no cookie manager was supplied at
+// commit time.
+IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CookiesBlockedForPdfProcess) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  WebContentsImpl* tab = static_cast<WebContentsImpl*>(shell()->web_contents());
+  GURL url = embedded_test_server()->GetURL("a.test", "/empty.html");
+
+  SetCookieDirect(tab, url, "A=1");
+  ASSERT_EQ("A=1", GetCookiesDirect(tab, url));
+
+  // Commit `url` as PDF content so that the resulting frame runs in a process
+  // whose SiteInfo has `is_pdf` set.
+  NavigationController::LoadURLParams params(url);
+  params.transition_type = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+  params.is_pdf = true;
+  NavigateToURLBlockUntilNavigationsComplete(
+      tab, params, 1, /*ignore_uncommitted_navigations=*/false);
+  ASSERT_TRUE(IsLastCommittedEntryOfPageType(tab, PAGE_TYPE_NORMAL));
+  ASSERT_EQ(url, tab->GetLastCommittedURL());
+
+  RenderFrameHost* frame = tab->GetPrimaryMainFrame();
+
+  // The PDF process must not be able to read or write cookies for the committed
+  // origin.
+  EXPECT_EQ("", GetCookieFromJS(frame));
+  std::ignore = EvalJs(frame, "document.cookie = 'B=2'");
+  EXPECT_EQ("A=1", GetCookiesDirect(tab, url));
+}
+
 IN_PROC_BROWSER_TEST_F(CookieBrowserTest, CookieNotReadableAfterExpiry) {
   ASSERT_TRUE(embedded_test_server()->Start());
Loading diff…

Original Bug Report

reported by vm...@google.com

PDF process isolation bypass via lazy RestrictedCookieManager binding

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A compromised PDF renderer process can bypass storage access controls by lazy-binding a RestrictedCookieManager interface. Although cookie/storage access is blocked for PDF processes on the eager navigation commit path, the lazy path via BrowserInterfaceBroker lacks any PDF-specific verification. This allows a compromised PDF renderer to retrieve and modify non-HttpOnly cookies for the hosting origin.

Affected files:

  • content/browser/renderer_host/render_frame_host_impl.cc
  • content/browser/browser_interface_binders.cc

Estimated timestamp from git blame: 2024-06-26

Root Cause Analysis

In Chromium, PDF documents are isolated into specialized, sandboxed renderer processes. These processes are intended to operate under a strict storage access block. ChildProcessSecurityPolicyImpl::IsAccessAllowedForPdfProcess explicitly returns false for AccessType::kCanAccessDataForCommittedOrigin to prevent PDF renderers from accessing passwords, storage, or cookies for the hosting origin.

To enforce this, the eager navigation commit-time path in RenderFrameHostImpl::SendCommitNavigation checks should_block_storage_access_for_pdf (based on GetSiteInstance()->GetSiteInfo().is_pdf()) and skips binding a RestrictedCookieManager:

// content/browser/renderer_host/render_frame_host_impl.cc
bool should_block_storage_access_for_pdf =
    GetSiteInstance()->GetSiteInfo().is_pdf();
...
if (common_params->url.SchemeIsHTTPOrHTTPS() && !origin_to_commit.opaque() &&
    !should_block_storage_access_for_pdf && ...) {
  cookie_manager_info = mojom::CookieManagerInfo::New();
  ...
  BindRestrictedCookieManagerWithOrigin(...);   // <-- Skipped for PDF
}

However, the lazy binding path—used when a renderer explicitly requests the interface via the BrowserInterfaceBroker—completely lacks any equivalent check.

In content/browser/browser_interface_binders.cc, the broker is registered without any PDF-specific constraints:

map->Add<network::mojom::RestrictedCookieManager>(
    &BindRenderFrameHostImpl<
        &RenderFrameHostImpl::BindRestrictedCookieManager>);

When invoked, this routes to RenderFrameHostImpl::BindRestrictedCookieManager, which binds the manager using the frame’s last committed origin without verifying whether the process is a PDF process, and without querying ChildProcessSecurityPolicy:

void RenderFrameHostImpl::BindRestrictedCookieManager(
    mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) {
  BindRestrictedCookieManagerWithOrigin(
      std::move(receiver), GetIsolationInfoForSubresources(),
      GetLastCommittedOrigin(), GetCookieSettingOverrides());
}

Because the subsequent layers in StoragePartitionImpl and NetworkContext assume that the browser process has already enforced access control before requesting the RestrictedCookieManager from the out-of-process Network Service, the interface is successfully bound and returned to the renderer.

Potential Attack Scenario

(Note: These are suggested/potential steps; our tooling does not have the ability to run code or verify a live exploit)

  1. An attacker uploads a malicious PDF file onto a target origin (e.g., https://victim.example/malicious.pdf).
  2. A user navigates to the PDF. The browser spawns an isolated PDF renderer process for it.
  3. The attacker exploits a memory corruption vulnerability (such as a PDFium bug) in the sandboxed PDF renderer process to achieve arbitrary code execution within the renderer.
  4. The compromised PDF renderer requests the ’network.mojom.RestrictedCookieManager’ interface via its BrowserInterfaceBroker pipe.
  5. The browser process maps the request to RenderFrameHostImpl::BindRestrictedCookieManager and binds it using the frame’s last committed origin (https://victim.example) without any PDF validation.
  6. The compromised PDF renderer uses the resulting RestrictedCookieManager interface to read and write non-HttpOnly cookies for https://victim.example, bypassing the PDF isolation storage restrictions.

Suggested Fix

Add a check in RenderFrameHostImpl::BindRestrictedCookieManager to prevent PDF frames from lazy-binding the interface. If GetSiteInstance()->GetSiteInfo().is_pdf() is true, the request should be blocked (and ideally, a bad message should be reported to terminate the compromised renderer):

void RenderFrameHostImpl::BindRestrictedCookieManager(
    mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) {
  if (GetSiteInstance()->GetSiteInfo().is_pdf()) {
    mojo::ReportBadMessage('PDF processes are not allowed to access cookies.');
    return;
  }
  BindRestrictedCookieManagerWithOrigin(
      std::move(receiver), GetIsolationInfoForSubresources(),
      GetLastCommittedOrigin(), GetCookieSettingOverrides());
}

Evaluated with Chrome root at commit: a2bea94528f4bd6cc57739c43fa3bb890b8367d3


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker