Chrome · DevTools › DevTools
CVE-2026-113247
Memory Corruption in DevTools (DevTools)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
buffer_usage_reporting_interval_content/browser/devtools/protocol/tracing_handler.cc |
modified | |
ifcontent/browser/devtools/protocol/tracing_handler.cc |
modified | |
ifcontent/browser/devtools/protocol/devtools_protocol_browsertest.cc |
modified |
Files Changed
content/browser/devtools/protocol/tracing_handler_unittest.cccontent/browser/devtools/browser_devtools_agent_host.cccontent/browser/devtools/protocol/tracing_handler.cccontent/browser/devtools/devtools_session.hcontent/browser/devtools/protocol/devtools_protocol_browsertest.cccontent/browser/devtools/protocol/tracing_handler.h
Patch
From d76b46e67426616a8da0e61ccf3c016e6fd0de93 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <[email protected]> Date: Mon, 28 Sep 2026 08:24:59 -0700 Subject: [PATCH] Reland "[DevTools] Restrict browser-process tracing and memory dumps to trusted clients" This is a reland of commit 68cfb6437ec8add0f36b9122d1047f3fe43454ce Original change's description: > [DevTools] Restrict browser-process tracing and memory dumps to trusted clients > > Previously, untrusted DevTools clients (such as extensions attached to a > single tab via `chrome.debugger`) could use the `Tracing` domain to > collect trace events from the browser and GPU processes or trigger global > memory dumps, potentially leaking cross-tab navigation URLs and > cross-process memory data. > This CL restricts `TracingHandler` when `!is_trusted_`: > - Excludes the browser process and GPU process PIDs from the Perfetto > process filter, and prevents the browser PID from being dynamically > added via `AddProcessToFilter()`. > - Clears any client-supplied `producer_name_filter` and > `producer_name_regex_filter` on Chrome data sources so untrusted > `perfettoConfig` payloads cannot bypass the process filter (using > `kNullProcessId` when `pids` is empty so Perfetto does not default to > matching all producers). > - Strips `org.chromium.memory_instrumentation` and > `org.chromium.native_heap_profiler` data sources from the trace config > and rejects `Tracing.requestMemoryDump` calls with `"Not allowed"`. > - Skips browser-side timeline frame/navigation trace event emission > (`EmitFrameTree`, `WillInitiatePrerender`, `ReadyToCommitNavigation`, > `FrameDeleted`) for untrusted sessions. > > Bug: 563814519, 514508203 > Change-Id: Id1d1a14522d6248046b6c150191da102407a4779 > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8425446 > Commit-Queue: Nikolay Vitkov <[email protected]> > Reviewed-by: Danil Somsikov <[email protected]> > Reviewed-by: Eric Seckler <[email protected]> > Cr-Commit-Position: refs/heads/main@{#1704813} Include-Ci-Only-Tests: chromium.fuchsia:fuchsia-x64-cast-receiver-rel|content_browsertests Bug: 563814519, 514508203 Change-Id: I514bba64577b2e42a0210011d17871cfcddff860 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8458647 Auto-Submit: Nikolay Vitkov <[email protected]> Reviewed-by: Danil Somsikov <[email protected]> Commit-Queue: Danil Somsikov <[email protected]> Reviewed-by: Eric Seckler <[email protected]> Cr-Commit-Position: refs/heads/main@{#1706139} --- diff --git a/content/browser/devtools/browser_devtools_agent_host.cc b/content/browser/devtools/browser_devtools_agent_host.cc index 5587e0f..639deec4 100644 --- a/content/browser/devtools/browser_devtools_agent_host.cc +++ b/content/browser/devtools/browser_devtools_agent_host.cc @@ -257,7 +257,8 @@ } session->CreateAndAddHandler<protocol::TracingHandler>( this, GetIOContext(), /* root_session */ nullptr, - session->GetClient()->IsTrusted()); + // Browser-target sessions are always trusted (enforced above). + /*is_trusted=*/true); #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) && BUILDFLAG(CLANG_PGO_PROFILING) session->CreateAndAddHandler<protocol::NativeProfilingHandler>(); diff --git a/content/browser/devtools/devtools_session.h b/content/browser/devtools/devtools_session.h index 0e95a1d..5235078 100644 --- a/content/browser/devtools/devtools_session.h +++ b/content/browser/devtools/devtools_session.h @@ -87,8 +87,8 @@ }; // For root sessions (see also private constructor for children). - DevToolsSession(DevToolsAgentHostClient* client, Mode mode); - ~DevToolsSession() override; + CONTENT_EXPORT DevToolsSession(DevToolsAgentHostClient* client, Mode mode); + CONTENT_EXPORT ~DevToolsSession() override; void SetAgentHost(DevToolsAgentHostImpl* agent_host); void SetRuntimeResumeCallback(base::OnceClosure runtime_resume); diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc index 52ca4b8..da05b1d9 100644 --- a/content/browser/devtools/protocol/tracing_handler.cc +++ b/content/browser/devtools/protocol/tracing_handler.cc @@ -253,6 +253,8 @@ return {}; } +const char kNeverMatchingProducerName[] = "org.chromium-0"; + bool IsChromeDataSource(const std::string& data_source_name) { return base::StartsWith(data_source_name, "org.chromium.") || data_source_name == "track_event"; @@ -571,6 +573,7 @@ return_as_stream_(false), gzip_compression_(false), buffer_usage_reporting_interval_(0) { + CHECK(is_trusted_ || session_for_process_filter_); video_consumer_ = std::make_unique<DevToolsVideoConsumer>(base::BindRepeating( &TracingHandler::OnFrameFromVideoConsumer, base::Unretained(this))); } @@ -789,6 +792,16 @@ proto_format); } + if (!is_trusted_) { + FilterUntrustedDataSources(trace_config); + if (!trace_config.data_sources_size()) { + callback->sendFailure(Response::InvalidParams( + "Supplied perfettoConfig doesn't have any allowed data sources " + "specified")); + return; + } + } + std::optional<perfetto::BackendType> backend = GetBackendTypeFromParameters( tracing_backend.value_or(Tracing::TracingBackendEnum::Auto), trace_config); @@ -857,21 +870,34 @@ screenshot_max_count_ = resolved_screenshot_max_count; if (session_for_process_filter_) { + std::erase_if(*trace_config_.mutable_data_sources(), + [](const perfetto::TraceConfig::DataSource& data_source) { + return !IsChromeDataSource(data_source.config().name()); + }); process_set_monitor_ = TracingProcessSetMonitor::Start( *session_for_process_filter_, base::BindRepeating(&TracingHandler::AddProcessToFilter, base::Unretained(this))); std::unordered_set<base::ProcessId> pids = process_set_monitor_->GetPids(); - base::ProcessId browser_pid = base::Process::Current().Pid(); - pids.insert(browser_pid); - if (auto* gpu_process_host = - GpuProcessHost::Get(GPU_PROCESS_KIND_SANDBOXED, - /* force_create */ false)) { - base::ProcessId gpu_pid = gpu_process_host->process_id(); - if (gpu_pid != base::kNullProcessId) { - pids.insert(gpu_pid); + if (is_trusted_) { + base::ProcessId browser_pid = base::Process::Current().Pid(); + pids.insert(browser_pid); + if (auto* gpu_process_host = + GpuProcessHost::Get(GPU_PROCESS_KIND_SANDBOXED, + /* force_create */ false)) { + base::ProcessId gpu_pid = gpu_process_host->process_id(); + if (gpu_pid != base::kNullProcessId) { + pids.insert(gpu_pid); + } } + } else { + pids.erase(base::Process::Current().Pid()); + } + + for (auto& data_source : *trace_config_.mutable_data_sources()) { + data_source.clear_producer_name_regex_filter(); + data_source.clear_producer_name_filter(); } AddPidsToProcessFilter(pids, trace_config_); } @@ -900,6 +926,9 @@ void TracingHandler::AddProcessToFilter(base::ProcessId pid) { CHECK(did_initiate_recording_); CHECK(session_); + if (!is_trusted_ && pid == base::Process::Current().Pid()) { + return; + } AddPidsToProcessFilter({pid}, trace_config_); session_->ChangeTraceConfig(trace_config_); } @@ -1045,6 +1074,10 @@ std::optional<bool> deterministic, std::optional<std::string> level_of_detail, std::unique_ptr<RequestMemoryDumpCallback> callback) { + if (!is_trusted_) { + callback->sendFailure(Response::ServerError("Not allowed")); + return; + } if (!IsTracing()) { callback->sendFailure(Response::ServerError("Tracing is not started")); return; @@ -1193,8 +1226,9 @@ void TracingHandler::ReadyToCommitNavigation( NavigationRequest* navigation_request) { - if (!did_initiate_recording_) + if (!did_initiate_recording_) { return; + } auto data = std::make_unique<base::trace_event::TracedValue>(); RenderFrameHostImpl* frame_host = navigation_request->GetRenderFrameHost(); FillFrameData(data.get(), frame_host, navigation_request->GetURL()); @@ -1208,8 +1242,9 @@ } void TracingHandler::FrameDeleted(FrameTreeNodeId frame_tree_node_id) { - if (!did_initiate_recording_) + if (!did_initiate_recording_) { return; + } FrameTreeNode* node = FrameTreeNode::GloballyFindByID(frame_tree_node_id); if (!node->current_frame_host()) { @@ -1225,16 +1260,21 @@ } // static +void TracingHandler::FilterUntrustedDataSources( + perfetto::TraceConfig& trace_config) { + std::erase_if( + *trace_config.mutable_data_sources(), + [](const perfetto::TraceConfig::DataSource& data_source) { + return data_source.config().name() != tracing::kMetaData2SourceName && + data_source.config().name() != kTrackEventDataSourceName; + }); +} + +// static void TracingHandler::AddPidsToProcessFilter( const std::unordered_set<base::ProcessId>& included_process_ids, perfetto::TraceConfig& trace_config) { for (auto& data_source : *(trace_config.mutable_data_sources())) { - auto* source_config = data_source.mutable_config(); - if (IsChromeDataSource(source_config->name())) { - if (data_source.producer_name_regex_filter_size() > 0) { - data_source.clear_producer_name_regex_filter(); - data_source.clear_producer_name_filter(); - } std::unordered_set<std::string> existing_filters( data_source.producer_name_filter().begin(), data_source.producer_name_filter().end()); @@ -1246,7 +1286,14 @@ *data_source.add_producer_name_filter() = std::move(new_filter); } } - } + + if (data_source.producer_name_filter_size() == 0) { + // An empty `producer_name_filter` in Perfetto matches all + // producers. Insert a never-matching producer name so the filter + // remains non-empty and matches no processes until a valid process + // is added. + *data_source.add_producer_name_filter() = kNeverMatchingProducerName; + } } } diff --git a/content/browser/devtools/protocol/tracing_handler.h b/content/browser/devtools/protocol/tracing_handler.h index 5a793bb..ae21dc94 100644 --- a/content/browser/devtools/protocol/tracing_handler.h +++ b/content/browser/devtools/protocol/tracing_handler.h @@ -146,6 +146,8 @@ CONTENT_EXPORT static void AddPidsToProcessFilter( const std::unordered_set<base::ProcessId>& included_process_ids, perfetto::TraceConfig& trace_config); + CONTENT_EXPORT static void FilterUntrustedDataSources( + perfetto::TraceConfig& trace_config); // Resolves the screenshot-capture parameters supplied to `Tracing.start`, // applying defaults when unset and validating that the combined memory // budget (`size * size * 4 * count`) does not exceed the per-session cap. @@ -201,6 +203,7 @@ FRIEND_TEST_ALL_PREFIXES(TracingHandlerTest, ProcessFilterClearsRegex); FRIEND_TEST_ALL_PREFIXES(TracingHandlerTest, ProcessFilterAppendsPids); FRIEND_TEST_ALL_PREFIXES(TracingHandlerTest, ResolveScreenshotParams); + FRIEND_TEST_ALL_PREFIXES(TracingHandlerTest, FilterUntrustedDataSources); }; } // namespace protocol diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc index d3729e6..7a957bb 100644 --- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc +++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc @@ -101,6 +101,7 @@ #include "net/test/test_doh_server.h" #include "services/network/public/cpp/features.h" #include "services/tracing/public/cpp/perfetto/perfetto_config.h" +#include "services/tracing/public/cpp/perfetto/perfetto_data_source_names.h" #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" #include "services/tracing/public/cpp/tracing_features.h" #include "testing/gmock/include/gmock/gmock.h" @@ -4708,6 +4709,189 @@ WaitForNotification("Tracing.tracingComplete", true); } +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, + TracingMemoryDumpRequiresTrustedClient) { + NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); + + SetIsTrusted(false); + Attach(); + + // Starting with a perfettoConfig containing only memory dump data sources + // should fail for an untrusted client because the disallowed data sources + // are stripped. + perfetto::TraceConfig memory_only_perfetto_config; + memory_only_perfetto_config.add_buffers()->set_size_kb(1024); + memory_only_perfetto_config.add_data_sources()->mutable_config()->set_name( + tracing::kMemoryInstrumentationDataSourceName); + memory_only_perfetto_config.add_data_sources()->mutable_config()->set_name( + tracing::kNativeHeapProfilerSourceName); + base::DictValue perfetto_start_params; + perfetto_start_params.Set( + "perfettoConfig", + base::Base64Encode(memory_only_perfetto_config.SerializeAsString())); + perfetto_start_params.Set("transferMode", "ReturnAsStream"); + EXPECT_FALSE( + SendCommandSync("Tracing.start", std::move(perfetto_start_params))); + EXPECT_THAT( + error()->FindInt("code"), + testing::Optional(static_cast<int>(crdtp::DispatchCode::INVALID_PARAMS))); + + base::DictValue trace_config; + base::ListValue categories; + categories.Append("disabled-by-default-memory-infra"); + trace_config.Set("includedCategories", std::move(categories)); + base::DictValue start_params; + start_params.Set("traceConfig", std::move(trace_config)); + start_params.Set("transferMode", "ReturnAsStream"); + EXPECT_TRUE(SendCommandSync("Tracing.start", start_params.Clone())); + + ASSERT_FALSE(SendCommandSync("Tracing.requestMemoryDump")); + EXPECT_THAT( + error()->FindInt("code"), + testing::Optional(static_cast<int>(crdtp::DispatchCode::SERVER_ERROR))); + EXPECT_EQ(*error()->FindString("message"), "Not allowed"); + + EXPECT_TRUE(SendCommandSync("Tracing.end")); + WaitForNotification("Tracing.tracingComplete", true); + + Detach(); + SetIsTrusted(true); + Attach(); + + EXPECT_TRUE(SendCommandSync("Tracing.start", std::move(start_params))); + // Request a "background" dump rather than the default "detailed" dump, + // because "detailed" dumps require OSMetrics::GetProcessMemoryMaps(), which + // is not implemented on Fuchsia (https://crbug.com/40720107). + base::DictValue dump_params; + dump_params.Set("levelOfDetail", "background"); + const base::DictValue* dump_result = + SendCommandSync("Tracing.requestMemoryDump", std::move(dump_params)); + ASSERT_TRUE(dump_result); + EXPECT_THAT(dump_result->FindBool("success"), testing::Optional(true)); + EXPECT_TRUE(SendCommandSync("Tracing.end")); + WaitForNotification("Tracing.tracingComplete", true); +} + +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, + TracingBrowserProcessRequiresTrustedClient) { + content::SetupCrossSiteRedirector(embedded_test_server()); + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html"); + GURL url_b = + embedded_test_server()->GetURL("b.com", "/title1.html?secret_tab_b"); + + NavigateToURLBlockUntilNavigationsComplete(shell(), url_a, 1); + + auto run_trace_and_read_json = [&](bool is_trusted, const GURL& url_b, + Shell** out_shell_b) -> std::string { + Detach(); + SetIsTrusted(is_trusted); + Attach(); + + base::trace_event::TraceConfig chrome_config( + "navigation,blink.user_timing," TRACE_DISABLED_BY_DEFAULT( + "devtools.timeline"), + ""); + perfetto::TraceConfig perfetto_config = + tracing::GetDefaultPerfettoConfig(chrome_config, + /*privacy_filtering_enabled=*/false, + /*convert_to_legacy_json=*/true); + // Attempt to explicitly request the browser process PID in + // producer_name_filter to verify that untrusted sessions cannot bypass + // the process filter. + std::string browser_producer = + base::StrCat({tracing::kPerfettoProducerNamePrefix, + base::NumberToString(static_cast<uint32_t>( + base::Process::Current().Pid()))}); + for (auto& ds : *perfetto_config.mutable_data_sources()) { + *ds.add_producer_name_filter() = browser_producer; + ds.add_producer_name_regex_filter(".*"); + } + + base::DictValue params; + params.Set("perfettoConfig", + base::Base64Encode(perfetto_config.SerializeAsString())); + params.Set("transferMode", "ReturnAsStream"); + + EXPECT_TRUE(SendCommandSync("Tracing.start", std::move(params))); + + // Create a new renderer mid-session to trigger AddProcessToFilter + Shell* shell_b = CreateBrowser(); + if (out_shell_b) { + *out_shell_b = shell_b; + } + + NavigateToURLBlockUntilNavigationsComplete(shell(), url_a, 1); + NavigateToURLBlockUntilNavigationsComplete(shell_b, url_b, 1); + EXPECT_TRUE(content::ExecJs(shell(), "performance.mark('mark_a');")); + EXPECT_TRUE(content::ExecJs(shell_b, "performance.mark('mark_b');")); ... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index d3729e6..7a957bb 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -101,6 +101,7 @@
#include "net/test/test_doh_server.h"
#include "services/network/public/cpp/features.h"
#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
+#include "services/tracing/public/cpp/perfetto/perfetto_data_source_names.h"
#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
#include "services/tracing/public/cpp/tracing_features.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -4708,6 +4709,189 @@
WaitForNotification("Tracing.tracingComplete", true);
}
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+ TracingMemoryDumpRequiresTrustedClient) {
+ NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1);
+
+ SetIsTrusted(false);
+ Attach();
+
+ // Starting with a perfettoConfig containing only memory dump data sources
+ // should fail for an untrusted client because the disallowed data sources
+ // are stripped.
+ perfetto::TraceConfig memory_only_perfetto_config;
+ memory_only_perfetto_config.add_buffers()->set_size_kb(1024);
+ memory_only_perfetto_config.add_data_sources()->mutable_config()->set_name(
+ tracing::kMemoryInstrumentationDataSourceName);
+ memory_only_perfetto_config.add_data_sources()->mutable_config()->set_name(
+ tracing::kNativeHeapProfilerSourceName);
+ base::DictValue perfetto_start_params;
+ perfetto_start_params.Set(
+ "perfettoConfig",
+ base::Base64Encode(memory_only_perfetto_config.SerializeAsString()));
+ perfetto_start_params.Set("transferMode", "ReturnAsStream");
+ EXPECT_FALSE(
+ SendCommandSync("Tracing.start", std::move(perfetto_start_params)));
+ EXPECT_THAT(
+ error()->FindInt("code"),
+ testing::Optional(static_cast<int>(crdtp::DispatchCode::INVALID_PARAMS)));
+
+ base::DictValue trace_config;
+ base::ListValue categories;
+ categories.Append("disabled-by-default-memory-infra");
+ trace_config.Set("includedCategories", std::move(categories));
+ base::DictValue start_params;
+ start_params.Set("traceConfig", std::move(trace_config));
+ start_params.Set("transferMode", "ReturnAsStream");
+ EXPECT_TRUE(SendCommandSync("Tracing.start", start_params.Clone()));
+
+ ASSERT_FALSE(SendCommandSync("Tracing.requestMemoryDump"));
+ EXPECT_THAT(
+ error()->FindInt("code"),
+ testing::Optional(static_cast<int>(crdtp::DispatchCode::SERVER_ERROR)));
+ EXPECT_EQ(*error()->FindString("message"), "Not allowed");
+
+ EXPECT_TRUE(SendCommandSync("Tracing.end"));
+ WaitForNotification("Tracing.tracingComplete", true);
+
+ Detach();
+ SetIsTrusted(true);
+ Attach();
+
+ EXPECT_TRUE(SendCommandSync("Tracing.start", std::move(start_params)));
+ // Request a "background" dump rather than the default "detailed" dump,
+ // because "detailed" dumps require OSMetrics::GetProcessMemoryMaps(), which
+ // is not implemented on Fuchsia (https://crbug.com/40720107).
+ base::DictValue dump_params;
+ dump_params.Set("levelOfDetail", "background");
+ const base::DictValue* dump_result =
+ SendCommandSync("Tracing.requestMemoryDump", std::move(dump_params));
+ ASSERT_TRUE(dump_result);
+ EXPECT_THAT(dump_result->FindBool("success"), testing::Optional(true));
+ EXPECT_TRUE(SendCommandSync("Tracing.end"));
+ WaitForNotification("Tracing.tracingComplete", true);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+ TracingBrowserProcessRequiresTrustedClient) {
+ content::SetupCrossSiteRedirector(embedded_test_server());
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
+ GURL url_b =
+ embedded_test_server()->GetURL("b.com", "/title1.html?secret_tab_b");
+
+ NavigateToURLBlockUntilNavigationsComplete(shell(), url_a, 1);
+
+ auto run_trace_and_read_json = [&](bool is_trusted, const GURL& url_b,
+ Shell** out_shell_b) -> std::string {
+ Detach();
+ SetIsTrusted(is_trusted);
+ Attach();
+
+ base::trace_event::TraceConfig chrome_config(
+ "navigation,blink.user_timing," TRACE_DISABLED_BY_DEFAULT(
+ "devtools.timeline"),
+ "");
+ perfetto::TraceConfig perfetto_config =
+ tracing::GetDefaultPerfettoConfig(chrome_config,
+ /*privacy_filtering_enabled=*/false,
+ /*convert_to_legacy_json=*/true);
+ // Attempt to explicitly request the browser process PID in
+ // producer_name_filter to verify that untrusted sessions cannot bypass
+ // the process filter.
+ std::string browser_producer =
+ base::StrCat({tracing::kPerfettoProducerNamePrefix,
+ base::NumberToString(static_cast<uint32_t>(
+ base::Process::Current().Pid()))});
+ for (auto& ds : *perfetto_config.mutable_data_sources()) {
+ *ds.add_producer_name_filter() = browser_producer;
+ ds.add_producer_name_regex_filter(".*");
+ }
+
+ base::DictValue params;
+ params.Set("perfettoConfig",
+ base::Base64Encode(perfetto_config.SerializeAsString()));
+ params.Set("transferMode", "ReturnAsStream");
+
+ EXPECT_TRUE(SendCommandSync("Tracing.start", std::move(params)));
+
+ // Create a new renderer mid-session to trigger AddProcessToFilter
+ Shell* shell_b = CreateBrowser();
+ if (out_shell_b) {
+ *out_shell_b = shell_b;
+ }
+
+ NavigateToURLBlockUntilNavigationsComplete(shell(), url_a, 1);
+ NavigateToURLBlockUntilNavigationsComplete(shell_b, url_b, 1);
+ EXPECT_TRUE(content::ExecJs(shell(), "performance.mark('mark_a');"));
+ EXPECT_TRUE(content::ExecJs(shell_b, "performance.mark('mark_b');"));
+
+ EXPECT_TRUE(SendCommandSync("Tracing.end"));
+
+ base::DictValue complete_notification =
+ WaitForNotification("Tracing.tracingComplete", true);
+ const std::string* stream_handle_ptr =
+ complete_notification.FindString("stream");
+ EXPECT_TRUE(stream_handle_ptr);
+ std::string stream_handle = stream_handle_ptr ? *stream_handle_ptr : "";
+
+ std::string trace_json;
+ bool eof = false;
+ while (!eof) {
+ base::DictValue read_params;
+ read_params.Set("handle", stream_handle);
+ const base::DictValue* response =
+ SendCommandSync("IO.read", std::move(read_params));
+ EXPECT_TRUE(response);
+ if (!response) {
+ break;
+ }
+ const std::string* data = response->FindString("data");
+ EXPECT_TRUE(data);
+ if (data) {
+ if (response->FindBool("base64Encoded").value_or(false)) {
+ std::string decoded;
+ EXPECT_TRUE(base::Base64Decode(*data, &decoded));
+ trace_json += decoded;
+ } else {
+ trace_json += *data;
+ }
+ }
+ eof = response->FindBool("eof").value_or(false);
+ }
+ return trace_json;
+ };
+
+ Shell* shell_b_untrusted = nullptr;
+ std::string untrusted_trace =
+ run_trace_and_read_json(/*is_trusted=*/false, url_b, &shell_b_untrusted);
+ EXPECT_THAT(untrusted_trace, testing::HasSubstr("mark_a"));
+ EXPECT_THAT(untrusted_trace, testing::Not(testing::HasSubstr("mark_b")));
+ EXPECT_THAT(untrusted_trace,
+ testing::Not(testing::HasSubstr("secret_tab_b")));
+ EXPECT_THAT(untrusted_trace,
+ testing::Not(testing::HasSubstr("TracingStartedInBrowser")));
+ EXPECT_THAT(untrusted_trace,
+ testing::Not(testing::HasSubstr("FrameCommittedInBrowser")));
+ if (shell_b_untrusted) {
+ shell_b_untrusted->Close();
+ }
+
+ GURL url_b2 =
+ embedded_test_server()->GetURL("b.com", "/title2.html?secret_tab_b2");
+ Shell* shell_b_trusted = nullptr;
+ std::string trusted_trace =
+ run_trace_and_read_json(/*is_trusted=*/true, url_b2, &shell_b_trusted);
+ EXPECT_THAT(trusted_trace, testing::HasSubstr("mark_a"));
+ EXPECT_THAT(trusted_trace, testing::HasSubstr("secret_tab_b2"));
+ EXPECT_THAT(trusted_trace, testing::HasSubstr("TracingStartedInBrowser"));
+ EXPECT_THAT(trusted_trace, testing::HasSubstr("FrameCommittedInBrowser"));
+ if (shell_b_trusted) {
+ shell_b_trusted->Close();
+ }
+}
+
IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, TracingPerfettoSiteIsolation) {
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
@@ -4841,8 +5025,9 @@
"System backend is not allowed for the current client");
}
-IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
- TracingAutoBackendNonChromeSourceRequiresTrustedClient) {
+IN_PROC_BROWSER_TEST_F(
+ DevToolsProtocolTest,
+ TracingUntrustedClientCannotProvideNonAllowlistedDataSource) {
perfetto::TraceConfig perfetto_config;
perfetto_config.add_buffers()->set_size_kb(1024);
perfetto_config.add_data_sources()->mutable_config()->set_name(
@@ -4862,9 +5047,10 @@
EXPECT_FALSE(SendCommandSync("Tracing.start", std::move(params)));
EXPECT_THAT(
error()->FindInt("code"),
- testing::Optional(static_cast<int>(crdtp::DispatchCode::SERVER_ERROR)));
+ testing::Optional(static_cast<int>(crdtp::DispatchCode::INVALID_PARAMS)));
EXPECT_EQ(*error()->FindString("message"),
- "System backend is not allowed for the current client");
+ "Supplied perfettoConfig doesn't have any allowed data sources "
+ "specified");
}
#if BUILDFLAG(IS_POSIX)
diff --git a/content/browser/devtools/protocol/tracing_handler_unittest.cc b/content/browser/devtools/protocol/tracing_handler_unittest.cc
index 2430a117..84dad65 100644
--- a/content/browser/devtools/protocol/tracing_handler_unittest.cc
+++ b/content/browser/devtools/protocol/tracing_handler_unittest.cc
@@ -7,8 +7,11 @@
#include <memory>
#include "base/json/json_reader.h"
+#include "base/process/process.h"
+#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_config.h"
#include "base/values.h"
+#include "content/browser/devtools/devtools_session.h"
#include "content/browser/devtools/devtools_traceable_screenshot.h"
#include "services/tracing/public/cpp/perfetto/perfetto_data_source_names.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -79,12 +82,23 @@
} // namespace
-class TracingHandlerTest : public testing::Test {
+class StubClient : public DevToolsAgentHostClient {
+ public:
+ void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
+ base::span<const uint8_t> message) override {}
+ void AgentHostClosed(DevToolsAgentHost* agent_host) override {}
+};
+
+class TracingHandlerTest : public testing::Test,
+ public testing::WithParamInterface<bool> {
public:
void SetUp() override {
+ client_ = std::make_unique<StubClient>();
+ session_ = std::make_unique<DevToolsSession>(
+ client_.get(), DevToolsSession::Mode::kSupportsTabTarget);
tracing_handler_ =
- std::make_unique<TracingHandler>(nullptr, nullptr, nullptr,
- /*is_trusted=*/true);
+ std::make_unique<TracingHandler>(nullptr, nullptr, session_.get(),
+ /*is_trusted=*/GetParam());
}
void TearDown() override { tracing_handler_.reset(); }
@@ -96,11 +110,33 @@
tracing_handler_->trace_data_buffer_state_.offset);
}
- private:
+ perfetto::TraceConfig GetTraceConfig() {
+ return tracing_handler_->trace_config_;
+ }
+
+ void SetDidInitiateRecording(bool value) {
+ tracing_handler_->did_initiate_recording_ = value;
+ }
+
+ void CallAddProcessToFilter(base::ProcessId pid) {
+ tracing_handler_->AddProcessToFilter(pid);
+ }
+
+ static void CallAddPidsToProcessFilter(
+ const std::unordered_set<base::ProcessId>& pids,
+ perfetto::TraceConfig& config) {
+ TracingHandler::AddPidsToProcessFilter(pids, config);
+ }
+
+ TracingHandler* tracing_handler() { return tracing_handler_.get(); }
+
+ protected:
+ std::unique_ptr<StubClient> client_;
+ std::unique_ptr<DevToolsSession> session_;
std::unique_ptr<TracingHandler> tracing_handler_;
};
... (truncated)
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page