CVE-2026-113136
Overview
Files Changed
content/browser/devtools/devtools_session.cc
Patch
From 611969dd519b88ca3cdce3c76dbb1668e67b5a3f Mon Sep 17 00:00:00 2001 From: Alex Rudenko <[email protected]> Date: Wed, 16 Sep 2026 01:10:02 -0700 Subject: [PATCH] Forward validated message_span to the client Forward the validated data copy instead of the original message->data. Fixed: 553114456 Change-Id: I00017b108c4c7a12a10149b83b554eb95088d735 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8409087 Commit-Queue: Alex Rudenko <[email protected]> Reviewed-by: Danil Somsikov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1698719} --- diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc index 54d3440..588f777 100644 --- a/content/browser/devtools/devtools_session.cc +++ b/content/browser/devtools/devtools_session.cc @@ -603,7 +603,7 @@ } return; } - client->DispatchProtocolMessage(agent_host, message->data); + client->DispatchProtocolMessage(agent_host, message_span); } void DevToolsSession::DispatchProtocolResponse(
Original Bug Report
BigBuffer TOCTOU in DevToolsSession::DispatchProtocolResponseOrNotification
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: DevToolsSession::DispatchProtocolResponseOrNotification makes a temporary copy of a shared-memory BigBuffer solely to perform message validation, but then forwards the original shared-memory buffer to the client. Because the sending renderer process retains a writable mapping to the shared memory region, a compromised renderer could rewrite message contents after validation succeeds but before the client processes them. This allows an attacker to bypass DevToolsMessage validation checks and spoof or forge protocol messages across DevTools sessions.
Affected files:
content/browser/devtools/devtools_session.cc
Estimated timestamp from git blame: 2026-06-15
Potential BigBuffer TOCTOU in DevToolsSession::DispatchProtocolResponseOrNotification
Location
content/browser/devtools/devtools_session.cc:576-607
Summary
In DevToolsSession::DispatchProtocolResponseOrNotification, messages backed by shared memory (mojo_base::BigBuffer::StorageType::kSharedMemory) are copied into a local std::vector<uint8_t> to validate them against spoofing attacks (e.g., verifying sessionId matches the child target). However, after ValidateMessage passes, the function passes the original message->data instead of the validated copy to client->DispatchProtocolMessage.
Because the sending (compromised) renderer retains writable access to the shared memory region backing the BigBuffer, it can modify the message contents in shared memory concurrently while or after validation runs, leading to a Time-of-Check to Time-of-Use (TOCTOU) validation bypass.
Detailed Analysis
In content/browser/devtools/devtools_session.cc:576-607:
void DevToolsSession::DispatchProtocolResponseOrNotification(
DevToolsAgentHostClient* client,
DevToolsAgentHostImpl* agent_host,
blink::mojom::DevToolsMessagePtr message,
const std::string& session_id,
const bool& is_notification) {
// If BigBuffer is backed by shared memory, make a copy so that a compromised
// renderer wouldn't be able to mess with the message as we validate it.
std::vector<uint8_t> message_bytes;
base::span<const uint8_t> message_span;
switch (message->data.storage_type()) {
case mojo_base::BigBuffer::StorageType::kBytes:
message_span = message->data.byte_span();
break;
case mojo_base::BigBuffer::StorageType::kSharedMemory:
message_bytes.assign(message->data.begin(), message->data.end());
message_span = message_bytes;
break;
default:
break;
}
const bool message_is_valid =
!message_span.empty() &&
ValidateMessage(session_id,
/*expected_has_id=*/!is_notification, message_span,
/*expect_cbor */ client->UsesBinaryProtocol());
if (!message_is_valid) {
if (RenderProcessHost* process_host = agent_host->GetProcessHost()) {
bad_message::ReceivedBadMessage(
process_host, bad_message::RFH_INCONSISTENT_DEVTOOLS_MESSAGE);
}
return;
}
client->DispatchProtocolMessage(agent_host, message->data);
}
- At line 585,
message_bytescreates a heap copy of the shared-memory-backedmessage->data. - At line 596,
ValidateMessageis executed onmessage_span(referencingmessage_bytes), ensuring the message does not illegally forgesessionIdoridattributes. - At line 606,
client->DispatchProtocolMessage(agent_host, message->data)passesmessage->data(implicitly converted tobase::span<const uint8_t>), which points to the live mapping (BigBuffer::data()inmojo/public/cpp/base/big_buffer.cc:116-118). - As documented in
mojo/public/cpp/base/big_buffer.h:74-78, Mojo shared memory regions may remain writable in the sender process. The sender can alter memory in the shared region before the client (such asTargetHandler::Session,DevToolsUIBindings, orDevToolsPipeHandler) copies or processes the span. - Downstream consumers do not perform any secondary validation on the message.
Potential Trigger Steps
(Note: These are suggested potential steps derived from static code analysis; automated dynamic proof of concept execution has not been run.)
- A compromised renderer with an active DevTools session creates a shared-memory backed
BigBufferforblink.mojom.DevToolsMessage. - The renderer retains a writable mapping to the shared memory region on a secondary thread.
- The renderer constructs a valid DevTools protocol message containing its assigned
sessionIdand a large payload (to widen the processing window inValidateMessage). - The renderer calls
DevToolsSessionHost::DispatchProtocolNotificationorDispatchProtocolResponse. - In the race window while
ValidateMessageevaluates the snapshot, the background thread in the renderer rewrites the shared memory buffer to stripsessionId, altersessionIdto another target, or inject response fields. ValidateMessagesucceeds based on the snapshot, and line 606 dispatches the tampered live shared buffer to the client, which accepts and routes the forged message.
Impact
A compromised renderer process (e.g. an out-of-process iframe) could forge protocol events or responses directed to the root target session (sessionId = '') or other target frames in flattened DevTools debugging mode, bypassing Site Isolation boundaries within the DevTools frontend and client tooling.
Suggested Fix
Ensure that the validated data (message_span / message_bytes) is dispatched to the client rather than the renderer-writable original buffer. For example:
if (message->data.storage_type() == mojo_base::BigBuffer::StorageType::kSharedMemory) {
client->DispatchProtocolMessage(agent_host, message_span);
} else {
client->DispatchProtocolMessage(agent_host, message->data);
}
Alternatively, replace message->data with mojo_base::BigBuffer(message_bytes) or pass message_span unconditionally to client->DispatchProtocolMessage.
Evaluated with Chrome root at commit: c6718ebbf2c677c6f63691e28c7801c563f6b19c
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.