Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in DevTools
DescriptionInappropriate implementation in DevTools
ComponentDevTools
Bug ClassLogic Error
Tracker496705691
Fix commitd0275cb2dc57 (chromium/src) +0/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • content/browser/devtools/protocol/tracing_handler.cc
From d0275cb2dc57814f9096f13a9024a5dd2a827986 Mon Sep 17 00:00:00 2001
From: Danil Somsikov <dsv@chromium.org>
Date: Thu, 23 Apr 2026 02:12:52 -0700
Subject: [PATCH] Remove unused processPseudoId field from DevTools tracing events.

The processPseudoId field was used as a temporary identifier to
correlate frames and processes in the browser before a real PID was
available. Investigation shows that this field is no longer used by the
modern DevTools frontend (it is not referenced in any .ts files).

Removing this field simplifies the tracing protocol and eliminates a
security concern where the field was leaking raw memory addresses of
RenderProcessHost objects.

Bug: 496705691
Change-Id: I6703fe1578916afba06ca16e56eda886d4e55145
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7776999
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Auto-Submit: Danil Somsikov <dsv@chromium.org>
Commit-Queue: Danil Somsikov <dsv@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1619393}
---

diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index d3f9aa3..8a54649 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -177,15 +177,11 @@
   base::WeakPtr<TracingHandler> tracing_handler_;
 };
 
-std::string GetProcessHostHex(RenderProcessHost* host) {
-  return base::StringPrintf("0x%" PRIxPTR, reinterpret_cast<uintptr_t>(host));
-}
 
 void SendProcessReadyInBrowserEvent(const base::UnguessableToken& frame_token,
                                     RenderProcessHost* host) {
   auto data = std::make_unique<base::trace_event::TracedValue>();
   data->SetString("frame", frame_token.ToString());
-  data->SetString("processPseudoId", GetProcessHostHex(host));
   data->SetInteger("processId", static_cast<int>(host->GetProcess().Pid()));
   TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
                        "ProcessReadyInBrowser", TRACE_EVENT_SCOPE_THREAD,
@@ -216,7 +212,6 @@
   RenderProcessHost* process_host = frame_host->GetProcess();
   const base::Process& process_handle = process_host->GetProcess();
   if (!process_handle.IsValid()) {
-    data->SetString("processPseudoId", GetProcessHostHex(process_host));
     frame_host->GetProcess()->PostTaskWhenProcessIsReady(
         base::BindOnce(&SendProcessReadyInBrowserEvent,
                        frame_host->devtools_frame_token(), process_host));
Loading diff…

Original Bug Report

reported by vi...@google.com

ASLR bypass: Browser-process heap address leak via CDP processPseudoId

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: The Chrome DevTools Protocol (CDP) Tracing domain leaks raw browser-process RenderProcessHost heap addresses to untrusted clients. This occurs because the processPseudoId field in trace events is populated using the raw pointer value of the host. An untrusted extension with the debugger permission can capture these events during cross-origin navigations, resulting in a deterministic browser-process ASLR bypass.

Affected files:

  • content/browser/devtools/protocol/tracing_handler.cc
  • content/browser/devtools/render_frame_devtools_agent_host.cc
  • content/browser/devtools/web_contents_devtools_agent_host.cc

Estimated timestamp from git blame: 2024-02-06

Description

The Chrome DevTools Protocol (CDP) TracingHandler leaks raw heap addresses from the highly privileged browser process.

In content/browser/devtools/protocol/tracing_handler.cc, the helper function GetProcessHostHex formats a raw RenderProcessHost* as a hexadecimal string:

std::string GetProcessHostHex(RenderProcessHost* host) {
  return base::StringPrintf("0x%" PRIxPTR, reinterpret_cast<uintptr_t>(host));
}

During a cross-origin navigation that requires a process switch, a new RenderProcessHost is provisioned. While its underlying OS process is launching asynchronously, process_handle.IsValid() temporarily returns false. If trace events (such as FrameCommittedInBrowser or ProcessReadyInBrowser) are generated during this window, FillFrameData uses GetProcessHostHex to populate the processPseudoId field with the raw browser-process heap address.

Crucially, untrusted DevTools clients (like Chrome Extensions with the debugger permission) are explicitly granted access to the TracingHandler. In content/browser/devtools/devtools_session.h, protocol::TracingHandler is whitelisted in IsDomainAvailableToUntrustedClient<T>(). This allows an extension to deterministically bypass Address Space Layout Randomization (ASLR) in the browser process, providing a powerful primitive for a sandbox escape chain.

Potential Steps to Trigger

(Note: These are suggested steps based on code analysis, as the Flapjack LLM agent does not yet have the ability to run code or execute a live proof of concept.)

  1. Install Malicious Extension: An attacker installs a Chrome extension possessing the debugger permission.
  2. Attach to Tab: The extension calls chrome.debugger.attach({tabId: target_tab}, "1.3") to attach to an active page.
  3. Start Tracing: The extension sends the Tracing.start CDP command with the disabled-by-default-devtools.timeline category.
  4. Trigger Process Switch: The extension initiates a cross-origin navigation in the attached tab (e.g., via chrome.tabs.update), forcing the creation of a new RenderProcessHost.
  5. Capture the Leak: As the navigation commits before the OS process is fully ready, the browser emits a FrameCommittedInBrowser trace event containing the raw RenderProcessHost* address in the processPseudoId field.
  6. Extract Pointer: The extension parses the DevTools event stream, reads the processPseudoId value, and uses the leaked heap pointer to bypass browser-process ASLR.

Suggested Fix

Do not use raw memory addresses as identifiers. Instead, use a safe, unique identifier.

A robust fix would be to modify GetProcessHostHex (or remove it entirely) and use the RenderProcessHost’s internal ID (host->GetID()) or generate a base::UnguessableToken specifically for pending processes, mapping it to the eventual OS PID when the process becomes ready.

Evaluated with Chrome root at commit: False


Results from Fortify so far have been promising, but it can be wrong in its deductions. At this time, it does not produce proof of concepts or fuzzer tests. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve Fortify’s 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