CVE-2026-93375
Overview
Background
- Elevated tracing service
- a privileged Windows service that Chrome asks, over COM/RPC, to run system-wide ETW tracing on its behalf.
- `NamedPlatformChannel`
- a Mojo channel backed by a named Windows pipe that is addressed through a global string name any local process can attempt to open or pre-create.
- `PlatformChannel`
- an anonymous Mojo channel backed by an unnamed pipe handle pair that is referenced by a concrete OS handle rather than by a name.
- Mojo invitation
- the handshake that bootstraps a Mojo IPC connection over a channel endpoint, with
MOJO_SEND_INVITATION_FLAG_ELEVATED/MOJO_ACCEPT_INVITATION_FLAG_ELEVATEDmarking the cross-privilege variant.
Root Cause Analysis
Before the patch, WindowsSystemTracingClientImpl::Host::Invite created a mojo::NamedPlatformChannel and passed only its string server name to the elevated service via trace_session->AcceptInvitation(channel.GetServerName().c_str(), &pid), and the service resolved that string with mojo::NamedPlatformChannel::ConnectToServer(server_name). Because a Windows named pipe lives in a global namespace, the service was resolving the client endpoint through an ambient, forgeable reference that was never tied to the identity of the actual RPC caller — a classic pipe-name-confusion window (the removed README lines confirm the old design: “passes its name to the session’s AcceptInvitation() method”).
The fix changes AcceptInvitation to take a raw handle value (UINT32 endpoint_handle) of an anonymous mojo::PlatformChannel remote endpoint; the service now obtains a handle to the RPC-verified calling process with GetCallingProcess (backed by I_RpcOpenClientProcess, which “implicitly verifies the calling client’s capabilities”) and ::DuplicateHandles that specific endpoint handle out of that exact process, then confirms via ::GetFileType(...) == FILE_TYPE_PIPE that it is really a pipe. This binds endpoint resolution to the concrete caller and its handle table, so there is no shared name for another process to squat or confuse. The interface IID and IDL uuid are also rotated (with the prior IIDs retained in old_tracing_service_iids) because the AcceptInvitation signature change is an ABI break.
Attack Path
- Observe the negotiation
A local, lower-privileged process learns that Chrome hands the elevated tracing service a named-pipe server name through
ISystemTraceSession::AcceptInvitation. - Interpose on the pipe name
The attacker pre-creates or races a named pipe matching the name the elevated service will resolve, so
NamedPlatformChannel::ConnectToServerbinds to the attacker’s endpoint instead of Chrome’s genuine one (inferred exact squatting/race mechanism; the patch shows only that resolution was by name). - Serve a crafted invitation
When the elevated service accepts an invitation with
MOJO_ACCEPT_INVITATION_FLAG_ELEVATEDover the hijacked pipe, it completes the Mojo handshake against the attacker rather than the real client. - Speak to the elevated service The attacker now holds a Mojo connection into the privileged tracing service and can drive its interfaces from a lower-privileged context (inferred).
Impact Assessment
LogicError — incorrect reference resolution in Tracing — and does not report in-the-wild exploitation.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/windows_services/elevated_tracing_service/system_tracing_session.cc |
modified |
Files Changed
chrome/windows_services/elevated_tracing_service/system_tracing_session_unittest.ccthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x86/tracing_service_idl.hchrome/windows_services/elevated_tracing_service/README.mdthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/arm64/tracing_service_idl_p.cthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x64/tracing_service_idl_p.cthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x86/tracing_service_idl.tlbthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x64/tracing_service_idl_i.cchrome/browser/tracing/windows_system_tracing_client_impl_unittest_win.ccchrome/install_static/chromium_install_modes.hchrome/windows_services/elevated_tracing_service/tracing_service_idl.idlthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x86/tracing_service_idl_p.cthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x64/tracing_service_idl.tlbchrome/windows_services/elevated_tracing_service/system_tracing_session.ccthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x86/tracing_service_idl_i.cthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/arm64/tracing_service_idl.tlbchrome/install_static/google_chrome_install_modes.hchrome/windows_services/elevated_tracing_service/elevated_tracing_service_unittest.ccchrome/browser/tracing/windows_system_tracing_client_impl_win.ccthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/x64/tracing_service_idl.hchrome/windows_services/elevated_tracing_service/system_tracing_session.hthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/arm64/tracing_service_idl.hthird_party/win_build_output/midl/chrome/windows_services/elevated_tracing_service/arm64/tracing_service_idl_i.c
Audit Directions
- Name-based IPC endpoint resolutionSearch for privileged services (COM/RPC servers, other
NamedPlatformChannelusers) that resolve pipes, channels, or objects by a caller-supplied string name rather than a handle bound to the verified caller. - Cross-privilege handle acquisitionVerify that handles crossing a privilege boundary are duplicated out of the RPC/COM-authenticated calling process (e.g. via
I_RpcOpenClientProcess/GetCallingProcess) and type-checked, rather than trusting an ambient reference. - ABI rotation on interface signature changesWhen a COM/IDL method signature changes, confirm the interface
uuid/IID is rotated and stale IIDs are tracked, so a mismatched client cannot invoke a differently-shaped server method.
Patch
From d0fe2599b7f437f6b4ffbe3acdaed36f83cccb26 Mon Sep 17 00:00:00 2001
From: Greg Thompson <grt@chromium.org>
Date: Fri, 04 Sep 2026 03:25:46 -0700
Subject: [PATCH] [etw tracing] AcceptInvitation via handle rather than name
Instead of using a NamedPlatformChannel for the invitation to the
tracing service, pass the handle value of a regular PlatformChannel and
duplicate it into the service. This side-steps any issues with pipe name
confusion.
Fixed: 550839154
Change-Id: I8c360a3d964d0bed388fc8d1a95ef08f70be74c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8285683
SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
Reviewed-by: Will Harris <wfh@chromium.org>
Commit-Queue: Greg Thompson <grt@chromium.org>
Reviewed-by: S Ganesh <ganesh@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1692319}
---
diff --git a/chrome/browser/tracing/windows_system_tracing_client_impl_win.cc b/chrome/browser/tracing/windows_system_tracing_client_impl_win.cc
index 046c7630..6875cd97 100644
--- a/chrome/browser/tracing/windows_system_tracing_client_impl_win.cc
+++ b/chrome/browser/tracing/windows_system_tracing_client_impl_win.cc
@@ -21,8 +21,9 @@
#include "base/task/thread_pool.h"
#include "base/win/com_init_util.h"
#include "base/win/win_util.h"
+#include "base/win/windows_handle_util.h"
#include "chrome/installer/util/install_service_work_item.h"
-#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
namespace {
@@ -104,14 +105,17 @@
base::expected<WindowsSystemTracingClientImpl::ServiceState, HRESULT>
WindowsSystemTracingClientImpl::Host::Invite(
Microsoft::WRL::ComPtr<ISystemTraceSession> trace_session) {
- mojo::NamedPlatformChannel channel(mojo::NamedPlatformChannel::Options{});
+ mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
invitation.set_extra_flags(MOJO_SEND_INVITATION_FLAG_ELEVATED);
mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(0);
DWORD pid = base::kNullProcessId;
- HRESULT hresult =
- trace_session->AcceptInvitation(channel.GetServerName().c_str(), &pid);
+ auto remote_endpoint = channel.TakeRemoteEndpoint();
+ HRESULT hresult = trace_session->AcceptInvitation(
+ base::win::HandleToUint32(
+ remote_endpoint.platform_handle().GetHandle().get()),
+ &pid);
RecordLaunchResult(LaunchStage::kAcceptInvitation, hresult);
if (FAILED(hresult)) {
return base::unexpected(hresult);
@@ -119,7 +123,7 @@
mojo::OutgoingInvitation::Send(std::move(invitation),
/*target_process=*/base::kNullProcessHandle,
- channel.TakeServerEndpoint());
+ channel.TakeLocalEndpoint());
trace_session_ = std::move(trace_session);
return base::ok(std::make_pair(pid, std::move(pipe)));
diff --git a/chrome/install_static/chromium_install_modes.h b/chrome/install_static/chromium_install_modes.h
index eba129b..a6d969d 100644
--- a/chrome/install_static/chromium_install_modes.h
+++ b/chrome/install_static/chromium_install_modes.h
@@ -33,6 +33,15 @@
NUM_INSTALL_MODES,
};
+inline constexpr auto kOldTracingServiceIids = std::to_array<IID>({
+ // Replaced in 2026-09. Delete after 2028-09.
+ // {A3FD580A-FFD4-4075-9174-75D0B199D3CB}
+ {0xa3fd580a,
+ 0xffd4,
+ 0x4075,
+ {0x91, 0x74, 0x75, 0xd0, 0xb1, 0x99, 0xd3, 0xcb}},
+});
+
inline constexpr auto kInstallModes = std::to_array<InstallConstants>({
// The primary (and only) install mode for Chromium.
{
@@ -79,13 +88,13 @@
0x447f,
{0x8b, 0xcc, 0x0e, 0x3f, 0x97, 0xbe, 0x9c,
0xf2}}, // SystemTraceSession CLSID.
- .tracing_service_iid = {0xa3fd580a,
- 0xffd4,
- 0x4075,
- {0x91, 0x74, 0x75, 0xd0, 0xb1, 0x99, 0xd3,
- 0xcb}}, // ISystemTraceSessionChromium IID and
+ .tracing_service_iid = {0xe0b03e2d,
+ 0x7682,
+ 0x4d83,
+ {0xb9, 0xff, 0x45, 0x74, 0xaf, 0x72, 0x05,
+ 0x00}}, // ISystemTraceSessionChromium IID and
// TypeLib
- .old_tracing_service_iids = {},
+ .old_tracing_service_iids = kOldTracingServiceIids,
.default_channel_name =
L"", // Empty default channel name since no update integration.
.channel_strategy = ChannelStrategy::UNSUPPORTED,
diff --git a/chrome/install_static/google_chrome_install_modes.h b/chrome/install_static/google_chrome_install_modes.h
index 5682b77..5164869 100644
--- a/chrome/install_static/google_chrome_install_modes.h
+++ b/chrome/install_static/google_chrome_install_modes.h
@@ -36,6 +36,42 @@
NUM_INSTALL_MODES,
};
+inline constexpr auto kOldTracingServiceIidsStable = std::to_array<IID>({
+ // Replaced in 2026-09. Delete after 2028-09.
+ // {056B3371-1C09-475B-A8D7-9E58BF45533E}
+ {0x056b3371,
+ 0x1c09,
+ 0x475b,
+ {0xa8, 0xd7, 0x9e, 0x58, 0xbf, 0x45, 0x53, 0x3e}},
+});
+
+inline constexpr auto kOldTracingServiceIidsBeta = std::to_array<IID>({
+ // Replaced in 2026-09. Delete after 2028-09.
+ // {A69D7D7D-9A08-422A-B6C6-B7B8D376A12C}
+ {0xa69d7d7d,
+ 0x9a08,
+ 0x422a,
+ {0xb6, 0xc6, 0xb7, 0xb8, 0xd3, 0x76, 0xa1, 0x2c}},
+});
+
+inline constexpr auto kOldTracingServiceIidsDev = std::to_array<IID>({
+ // Replaced in 2026-09. Delete after 2028-09.
+ // {E08ADAE8-9334-46ED-B0CF-DD1780158D55}
+ {0xe08adae8,
+ 0x9334,
+ 0x46ed,
+ {0xb0, 0xcf, 0xdd, 0x17, 0x80, 0x15, 0x8d, 0x55}},
+});
+
+inline constexpr auto kOldTracingServiceIidsSxS = std::to_array<IID>({
+ // Replaced in 2026-09. Delete after 2028-09.
+ // {6EFB8558-68D1-4826-A612-A180B3570375}
+ {0x6efb8558,
+ 0x68d1,
+ 0x4826,
+ {0xa6, 0x12, 0xa1, 0x80, 0xb3, 0x57, 0x03, 0x75}},
+});
+
inline constexpr auto kInstallModes = std::to_array<InstallConstants>({
// The primary install mode for stable Google Chrome.
{
@@ -81,13 +117,13 @@
0x462c,
{0xb2, 0xe0, 0x49, 0xa7, 0x71, 0x48, 0x7e,
0x46}}, // SystemTraceSession CLSID.
- .tracing_service_iid = {0x056b3371,
- 0x1c09,
- 0x475b,
- {0xa8, 0xd7, 0x9e, 0x58, 0xbf, 0x45, 0x53,
- 0x3e}}, // ISystemTraceSessionChromium IID
+ .tracing_service_iid = {0xa780c41e,
+ 0x1d88,
+ 0x4e7c,
+ {0x98, 0xf9, 0xb0, 0x68, 0x96, 0x68, 0x05,
+ 0x5c}}, // ISystemTraceSessionChromium IID
// and TypeLib
- .old_tracing_service_iids = {},
+ .old_tracing_service_iids = kOldTracingServiceIidsStable,
.default_channel_name = L"", // The empty string means "stable".
.channel_strategy = ChannelStrategy::FLOATING,
.supports_system_level = true, // Supports system-level installs.
@@ -149,13 +185,13 @@
0x467e,
{0x9f, 0x61, 0x86, 0x16, 0xac, 0x13, 0xd1,
0xef}}, // SystemTraceSession CLSID.
- .tracing_service_iid = {0xa69d7d7d,
- 0x9a08,
- 0x422a,
- {0xb6, 0xc6, 0xb7, 0xb8, 0xd3, 0x76, 0xa1,
- 0x2c}}, // ISystemTraceSessionChromium IID and
+ .tracing_service_iid = {0x14f7041d,
+ 0x19e4,
+ 0x4f7f,
+ {0xab, 0x6c, 0x85, 0x8e, 0x09, 0xde, 0x9f,
+ 0x97}}, // ISystemTraceSessionChromium IID and
// TypeLib
- .old_tracing_service_iids = {},
+ .old_tracing_service_iids = kOldTracingServiceIidsBeta,
.default_channel_name = L"beta", // Forced channel name.
.channel_strategy = ChannelStrategy::FIXED,
.supports_system_level = true, // Supports system-level installs.
@@ -224,13 +260,13 @@
0x4cdc,
{0x83, 0x40, 0x85, 0x29, 0xab, 0x83, 0xc5,
0x52}}, // SystemTraceSession CLSID.
- .tracing_service_iid = {0xe08adae8,
- 0x9334,
- 0x46ed,
- {0xb0, 0xcf, 0xdd, 0x17, 0x80, 0x15, 0x8d,
- 0x55}}, // ISystemTraceSessionChromium IID and
+ .tracing_service_iid = {0xaefb2e52,
+ 0xd121,
+ 0x4617,
+ {0xa3, 0x66, 0xdd, 0x78, 0x22, 0x46, 0xfb,
+ 0x4b}}, // ISystemTraceSessionChromium IID and
// TypeLib
- .old_tracing_service_iids = {},
+ .old_tracing_service_iids = kOldTracingServiceIidsDev,
.default_channel_name = L"dev", // Forced channel name.
.channel_strategy = ChannelStrategy::FIXED,
.supports_system_level = true, // Supports system-level installs.
@@ -298,13 +334,13 @@
0x4cdc,
{0x83, 0x40, 0x85, 0x29, 0xab, 0x83, 0xc5,
0x52}}, // SystemTraceSession CLSID.
- .tracing_service_iid = {0x6efb8558,
- 0x68d1,
- 0x4826,
- {0xa6, 0x12, 0xa1, 0x80, 0xb3, 0x57, 0x03,
- 0x75}}, // ISystemTraceSessionChromium IID and
+ .tracing_service_iid = {0x4a5732f2,
+ 0xdc92,
+ 0x4ee4,
+ {0xb8, 0xf4, 0xa3, 0x21, 0x69, 0x67, 0x31,
+ 0x2a}}, // ISystemTraceSessionChromium IID and
// TypeLib
- .old_tracing_service_iids = {},
+ .old_tracing_service_iids = kOldTracingServiceIidsSxS,
.default_channel_name = L"canary", // Forced channel name.
.channel_strategy = ChannelStrategy::FIXED,
.supports_system_level =
diff --git a/chrome/windows_services/elevated_tracing_service/README.md b/chrome/windows_services/elevated_tracing_service/README.md
index 8c5611887..4bbfec6 100644
--- a/chrome/windows_services/elevated_tracing_service/README.md
+++ b/chrome/windows_services/elevated_tracing_service/README.md
@@ -57,11 +57,11 @@
A session becomes active via a multi-step negotiation.
1. Chrome creates an instance of `SystemTracingSession`.
-2. Chrome creates a named mojo channel to establish a connection with the
- service and passes its name to the session's `AcceptInvitation()` method.
-3. The service begins monitoring the client (Chrome) for termination, connects
- to the named channel to accept the invitation from it, and returns its PID to
- the client.
+2. Chrome creates a mojo channel to establish a connection with the service and
+ passes its platform handle to the session's `AcceptInvitation()` method.
+3. The service begins monitoring the client (Chrome) for termination, duplicates
+ the channel handle into its process to accept the invitation from it, and
+ returns its PID to the client.
4. Chrome sends a mojo invitation with an initial message pipe (named `0`) over
the channel to the service.
5. The service takes its end of the message pipe from the invitation, wraps it
diff --git a/chrome/windows_services/elevated_tracing_service/system_tracing_session.cc b/chrome/windows_services/elevated_tracing_service/system_tracing_session.cc
index 612fd23..48cd34d 100644
--- a/chrome/windows_services/elevated_tracing_service/system_tracing_session.cc
+++ b/chrome/windows_services/elevated_tracing_service/system_tracing_session.cc
@@ -11,6 +11,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/task/sequenced_task_runner.h"
+#include "base/win/windows_handle_util.h"
#include "chrome/windows_services/elevated_tracing_service/service_integration.h"
#include "chrome/windows_services/service_program/crash_reporting.h"
#include "chrome/windows_services/service_program/get_calling_process.h"
@@ -18,8 +19,8 @@
#include "chrome/windows_services/service_program/user_crash_state.h"
#include "components/tracing/common/etw_system_data_source_win.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
#include "mojo/public/cpp/system/handle.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
@@ -54,9 +55,10 @@
}
// Invoked on an arbitrary RPC thread.
-HRESULT SystemTracingSession::AcceptInvitation(const wchar_t* server_name,
+HRESULT SystemTracingSession::AcceptInvitation(UINT32 endpoint_handle,
DWORD* pid) {
- if (!pid || !server_name || !*server_name) {
+ if (!pid || endpoint_handle == 0 ||
+ base::win::IsPseudoHandle(base::win::Uint32ToHandle(endpoint_handle))) {
return E_INVALIDARG;
}
*pid = base::kNullProcessId;
@@ -65,33 +67,55 @@
return kErrorSessionAlreadyActive;
}
- // Impersonate the client to get a handle to the client's process and per-user
- // state related to crash handling.
- base::Process client_process;
+ // Obtain a handle to the client's process. I_RpcOpenClientProcess implicitly
+ // verifies the calling client's capabilities. Calling this while
+ // impersonating the client can fail or mask the RPC context.
+ base::Process client_process = GetCallingProcess(
+ PROCESS_DUP_HANDLE | PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE);
+ if (!client_process.IsValid()) {
+ return kErrorCouldNotObtainCallingProcess;
+ }
+
+ // Impersonate the client to get per-user state related to crash handling.
std::unique_ptr<UserCrashState> user_crash_state;
if (ScopedClientImpersonation impersonate; impersonate.is_valid()) {
- client_process = GetCallingProcess();
- if (!client_process.IsValid()) {
- return kErrorCouldNotObtainCallingProcess;
- }
user_crash_state = UserCrashState::Create(impersonate, client_process);
} else {
return impersonate.result();
}
- // Get a handle to the client process with appropriate rights.
const auto client_pid = client_process.Pid();
- if (client_pid != base::kNullProcessId) {
- if (auto dup = base::Process::OpenWithAccess(client_pid, SYNCHRONIZE);
- dup.IsValid()) {
- std::swap(client_process, dup);
- } else {
- return kErrorCouldNotOpenCallingProcess;
- }
- } else {
+ if (client_pid == base::kNullProcessId) {
return kErrorCouldNotGetCallingProcessPid;
}
+ // Duplicate the client's message pipe handle into the service process.
+ HANDLE raw_handle = nullptr;
+ if (!::DuplicateHandle(client_process.Handle(),
+ base::win::Uint32ToHandle(endpoint_handle),
+ ::GetCurrentProcess(), &raw_handle,
+ /*dwDesiredAccess=*/0,
+ /*bInheritHandle=*/FALSE, DUPLICATE_SAME_ACCESS)) {
+ return E_INVALIDARG; // Invalid endpoint handle.
+ }
+ mojo::PlatformChannelEndpoint endpoint(mojo::PlatformHandle(
+ base::win::ScopedHandle(std::exchange(raw_handle, nullptr))));
+ if (::GetFileType(endpoint.platform_handle().GetHandle().get()) !=
+ FILE_TYPE_PIPE) {
+ return E_INVALIDARG; // Invalid endpoint handle.
+ }
+
+ // Drop down to only SYNCHRONIZE on the child process's handle. Pass ownership
+ // of `client_process`, as DUPLICATE_CLOSE_SOURCE ensures that it will be
+ // closed even on failure.
+ HANDLE raw_client_handle = nullptr;
+ if (!::DuplicateHandle(::GetCurrentProcess(), client_process.Release(),
+ ::GetCurrentProcess(), &raw_client_handle, SYNCHRONIZE,
+ /*bInheritHandle=*/FALSE, DUPLICATE_CLOSE_SOURCE)) {
+ return kErrorCouldNotDuplicateCallingProcessHandle;
+ }
+ client_process = base::Process(std::exchange(raw_client_handle, nullptr));
+
// This instance is ready to become the active session provided that there
// isn't one already.
auto session = SessionRegistry::RegisterActiveSession(
@@ -108,10 +132,6 @@
/*process_type=*/"elevated-tracing-service", task_runner_);
}
- auto endpoint = mojo::NamedPlatformChannel::ConnectToServer(server_name);
- if (!endpoint.is_valid()) {
- return E_INVALIDARG; // Invalid channel name.
- }
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
std::move(endpoint), MOJO_ACCEPT_INVITATION_FLAG_ELEVATED);
if (!invitation.is_valid()) {
diff --git a/chrome/windows_services/elevated_tracing_service/system_tracing_session.h b/chrome/windows_services/elevated_tracing_service/system_tracing_session.h
index f390e43ac..a4159c0 100644
--- a/chrome/windows_services/elevated_tracing_service/system_tracing_session.h
+++ b/chrome/windows_services/elevated_tracing_service/system_tracing_session.h
@@ -54,8 +54,7 @@
scoped_refptr<base::SequencedTaskRunner> task_runner);
// ISystemTraceSession:
- IFACEMETHODIMP AcceptInvitation(const wchar_t* channel_name,
- DWORD* pid) override;
+ IFACEMETHODIMP AcceptInvitation(UINT32 endpoint_handle, DWORD* pid) override;
private:
~SystemTracingSession() override;
diff --git a/chrome/windows_services/elevated_tracing_service/tracing_service_idl.idl b/chrome/windows_services/elevated_tracing_service/tracing_service_idl.idl
index df74160..ac55d8305 100644
--- a/chrome/windows_services/elevated_tracing_service/tracing_service_idl.idl
+++ b/chrome/windows_services/elevated_tracing_service/tracing_service_idl.idl
@@ -8,7 +8,7 @@
[
object,
oleautomation,
- uuid(DB01E5CE-10CE-4A84-8FAE-DA5E46EEF1CF),
+ uuid(CF38F35B-1913-4214-AC70-6E39C9729DA3),
helpstring("ISystemTraceSession Interface"),
pointer_default(unique)
]
@@ -17,14 +17,13 @@
// ISystemTraceSession will require a security review.
interface ISystemTraceSession : IUnknown
... (file diff truncated)
Regression Test / PoC
diff --git a/chrome/browser/tracing/windows_system_tracing_client_impl_unittest_win.cc b/chrome/browser/tracing/windows_system_tracing_client_impl_unittest_win.cc
index d1c1a7458..b3f22db 100644
--- a/chrome/browser/tracing/windows_system_tracing_client_impl_unittest_win.cc
+++ b/chrome/browser/tracing/windows_system_tracing_client_impl_unittest_win.cc
@@ -12,7 +12,6 @@
#include "chrome/browser/tracing/windows_system_tracing_client_impl_win.h"
#include "chrome/windows_services/elevated_tracing_service/tracing_service_idl.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,7 +31,7 @@
public:
MOCK_METHOD(HRESULT,
AcceptInvitation,
- (const wchar_t* server_name, DWORD* pid),
+ (UINT32 endpoint_handle, DWORD* pid),
(override, Calltype(STDMETHODCALLTYPE)));
MOCK_METHOD(HRESULT,
QueryInterface,
@@ -73,12 +72,12 @@
StrictMock<MockSystemTraceSession> session;
// Configure a mock tracing session that will return the current process's PID
- // and capture the server name to the channel on which the invitation will be
- // sent.
- mojo::NamedPlatformChannel::ServerName server_name;
+ // and capture the handle value for the channel on which the invitation will
+ // be sent.
+ UINT32 handle_val = 0;
EXPECT_CALL(session, AcceptInvitation(_, _))
- .WillOnce([&server_name](const wchar_t* name, DWORD* pid) {
- server_name = name;
+ .WillOnce([&handle_val](UINT32 val, DWORD* pid) {
+ handle_val = val;
*pid = ::GetCurrentProcessId();
return S_OK;
});
@@ -99,8 +98,8 @@
histogram_tester.ExpectUniqueSample(
"Tracing.ElevatedTracingService.LaunchResult.AcceptInvitation", S_OK, 1);
- // The host should have given the service a valid server name.
- ASSERT_FALSE(server_name.empty());
+ // The host should have given the service a valid handle.
+ ASSERT_NE(handle_val, 0u);
}
// Tests that the host properly handles the case where the service reports a
diff --git a/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_unittest.cc b/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_unittest.cc
index b499b8eb..390c978 100644
--- a/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_unittest.cc
+++ b/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_unittest.cc
@@ -25,13 +25,15 @@
#include "base/win/elevation_util.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/win_util.h"
+#include "base/win/windows_handle_util.h"
#include "chrome/windows_services/elevated_tracing_service/system_tracing_session.h"
#include "chrome/windows_services/elevated_tracing_service/tracing_service_idl.h"
#include "chrome/windows_services/elevated_tracing_service/with_child_test.h"
#include "chrome/windows_services/service_program/test_support/scoped_medium_integrity.h"
#include "chrome/windows_services/service_program/test_support/service_environment.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/public/cpp/system/simple_watcher.h"
@@ -56,19 +58,22 @@
COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING));
- mojo::NamedPlatformChannel channel(mojo::NamedPlatformChannel::Options{});
+ mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
invitation.set_extra_flags(MOJO_SEND_INVITATION_FLAG_ELEVATED);
mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(/*name=*/0);
+ auto remote_endpoint = channel.TakeRemoteEndpoint();
DWORD pid = base::kNullProcessId;
- ASSERT_HRESULT_SUCCEEDED(
- trace_session->AcceptInvitation(channel.GetServerName().c_str(), &pid));
+ ASSERT_HRESULT_SUCCEEDED(trace_session->AcceptInvitation(
+ base::win::HandleToUint32(
+ remote_endpoint.platform_handle().GetHandle().get()),
+ &pid));
ASSERT_NE(pid, base::kNullProcessId);
mojo::OutgoingInvitation::Send(std::move(invitation),
/*target_process=*/base::kNullProcessHandle,
- channel.TakeServerEndpoint());
+ channel.TakeLocalEndpoint());
// Wait for the service to connect to the pipe.
mojo::SimpleWatcher pipe_watcher(FROM_HERE,
@@ -112,32 +117,38 @@
mojo::ScopedMessagePipeHandle pipe;
{
- mojo::NamedPlatformChannel channel(mojo::NamedPlatformChannel::Options{});
+ mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
invitation.set_extra_flags(MOJO_SEND_INVITATION_FLAG_ELEVATED);
pipe = invitation.AttachMessagePipe(/*name=*/0);
+ auto remote_endpoint = channel.TakeRemoteEndpoint();
DWORD pid = base::kNullProcessId;
- ASSERT_HRESULT_SUCCEEDED(
- trace_session->AcceptInvitation(channel.GetServerName().c_str(), &pid));
+ ASSERT_HRESULT_SUCCEEDED(trace_session->AcceptInvitation(
+ base::win::HandleToUint32(
+ remote_endpoint.platform_handle().GetHandle().get()),
+ &pid));
ASSERT_NE(pid, base::kNullProcessId);
mojo::OutgoingInvitation::Send(std::move(invitation),
/*target_process=*/base::kNullProcessHandle,
- channel.TakeServerEndpoint());
+ channel.TakeLocalEndpoint());
}
{
- mojo::NamedPlatformChannel channel(mojo::NamedPlatformChannel::Options{});
+ mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
invitation.set_extra_flags(MOJO_SEND_INVITATION_FLAG_ELEVATED);
mojo::ScopedMessagePipeHandle pipe2 =
invitation.AttachMessagePipe(/*name=*/0);
+ auto remote_endpoint = channel.TakeRemoteEndpoint();
DWORD pid = base::kNullProcessId;
- ASSERT_EQ(
- trace_session->AcceptInvitation(channel.GetServerName().c_str(), &pid),
- kErrorSessionAlreadyActive);
+ ASSERT_EQ(trace_session->AcceptInvitation(
+ base::win::HandleToUint32(
+ remote_endpoint.platform_handle().GetHandle().get()),
+ &pid),
+ kErrorSessionAlreadyActive);
}
}
@@ -160,19 +171,22 @@
mojo::ScopedMessagePipeHandle pipe;
{
- mojo::NamedPlatformChannel channel(mojo::NamedPlatformChannel::Options{});
+ mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
invitation.set_extra_flags(MOJO_SEND_INVITATION_FLAG_ELEVATED);
pipe = invitation.AttachMessagePipe(/*name=*/0);
+ auto remote_endpoint = channel.TakeRemoteEndpoint();
DWORD pid = base::kNullProcessId;
- ASSERT_HRESULT_SUCCEEDED(
- trace_session->AcceptInvitation(channel.GetServerName().c_str(), &pid));
+ ASSERT_HRESULT_SUCCEEDED(trace_session->AcceptInvitation(
+ base::win::HandleToUint32(
+ remote_endpoint.platform_handle().GetHandle().get()),
+ &pid));
ASSERT_NE(pid, base::kNullProcessId);
mojo::OutgoingInvitation::Send(std::move(invitation),
/*target_process=*/base::kNullProcessHandle,
- channel.TakeServerEndpoint());
+ channel.TakeLocalEndpoint());
}
// Now a second client is refused.
@@ -189,16 +203,43 @@
COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING));
- mojo::NamedPlatformChannel channel(mojo::NamedPlatformChannel::Options{});
+ mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
invitation.set_extra_flags(MOJO_SEND_INVITATION_FLAG_ELEVATED);
mojo::ScopedMessagePipeHandle pipe2 =
invitation.AttachMessagePipe(/*name=*/0);
+ auto remote_endpoint = channel.TakeRemoteEndpoint();
DWORD pid = base::kNullProcessId;
- ASSERT_EQ(
- trace_session2->AcceptInvitation(channel.GetServerName().c_str(), &pid),
- kErrorSessionInProgress);
+ ASSERT_EQ(trace_session2->AcceptInvitation(
+ base::win::HandleToUint32(
+ remote_endpoint.platform_handle().GetHandle().get()),
+ &pid),
+ kErrorSessionInProgress);
+}
+
+void TestInvalidHandle() {
+ Microsoft::WRL::ComPtr<IUnknown> unknown;
+ ASSERT_HRESULT_SUCCEEDED(::CoCreateInstance(
+ elevated_tracing_service::kTestSystemTracingSessionClsid, nullptr,
+ CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&unknown)));
+
+ Microsoft::WRL::ComPtr<ISystemTraceSession> trace_session;
+ ASSERT_HRESULT_SUCCEEDED(unknown.As(&trace_session));
+ unknown.Reset();
+
+ ASSERT_HRESULT_SUCCEEDED(::CoSetProxyBlanket(
+ trace_session.Get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT,
+ COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+ RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING));
+
+ DWORD pid = base::kNullProcessId;
+
+ // An event is not a pipe.
+ base::win::ScopedHandle event(::CreateEvent(nullptr, TRUE, FALSE, nullptr));
+ ASSERT_EQ(trace_session->AcceptInvitation(
+ base::win::HandleToUint32(event.get()), &pid),
+ E_INVALIDARG);
}
MULTIPROCESS_TEST_MAIN(GetServiceProcessHandleInChild) {
@@ -229,6 +270,19 @@
return ::testing::Test::HasFailure() ? 1 : 0;
}
+MULTIPROCESS_TEST_MAIN(InvalidHandleInChild) {
+ // base::debug::WaitForDebugger(30, /*silent=*/false);
+
+ base::win::ScopedCOMInitializer com_initializer;
+ if (!com_initializer.Succeeded()) {
+ return 1;
+ }
+
+ TestInvalidHandle();
+
+ return ::testing::Test::HasFailure() ? 1 : 0;
+}
+
MULTIPROCESS_TEST_MAIN(OnlyOneSessionInChild) {
// base::debug::WaitForDebugger(30, /*silent=*/false);
@@ -327,6 +381,18 @@
TestOnlyOneSession();
}
+// Tests calling into the service from the main test process itself after
+// dropping to medium integrity level.
+TEST_F(TracingServiceTest, InvalidHandle) {
+ base::win::ScopedCOMInitializer com_initializer;
+ ASSERT_TRUE(com_initializer.Succeeded());
+
+ ScopedMediumIntegrity medium_integrity;
+ ASSERT_TRUE(medium_integrity.Succeeded());
+
+ TestInvalidHandle();
+}
+
// Tests calling into the service from a de-elevated child process. This test is
// skipped if UAC is disabled.
TEST_F(TracingServiceTest, GetServiceProcessHandleInChild) {
@@ -349,8 +415,7 @@
TEST_F(TracingServiceTest, OnlyOneInvitationInChild) {
// Pro tip: Add --show-error-dialogs to the test's command line and put
// base::debug::WaitForDebugger(30, /*silent=*/false); in
- // `GetServiceProcessHandleInChild()` to more easily debug in the child
- // process.
+ // `OnlyOneInvitationInChild()` to more easily debug in the child process.
auto child_or_error = RunInChildDeElevated("OnlyOneInvitationInChild");
if (!child_or_error.has_value()) {
@@ -366,8 +431,7 @@
TEST_F(TracingServiceTest, OnlyOneSessionInChild) {
// Pro tip: Add --show-error-dialogs to the test's command line and put
// base::debug::WaitForDebugger(30, /*silent=*/false); in
- // `GetServiceProcessHandleInChild()` to more easily debug in the child
- // process.
+ // `OnlyOneSessionInChild()` to more easily debug in the child process.
auto child_or_error = RunInChildDeElevated("OnlyOneSessionInChild");
if (!child_or_error.has_value()) {
@@ -378,6 +442,22 @@
ASSERT_EQ(exit_code, 0);
}
+// Tests calling into the service from a de-elevated child process. This test is
+// skipped if UAC is disabled.
+TEST_F(TracingServiceTest, InvalidHandleInChild) {
+ // Pro tip: Add --show-error-dialogs to the test's command line and put
+ // base::debug::WaitForDebugger(30, /*silent=*/false); in
+ // `InvalidHandleInChild()` to more easily debug in the child process.
+ auto child_or_error = RunInChildDeElevated("InvalidHandleInChild");
+
+ if (!child_or_error.has_value()) {
+ GTEST_SKIP() << "Cannot de-elevate when UAC is disabled.";
+ }
+ int exit_code;
+ ASSERT_TRUE(child_or_error->WaitForExit(&exit_code));
+ ASSERT_EQ(exit_code, 0);
+}
+
MULTIPROCESS_TEST_MAIN(ConnectToServiceAndTerminateInChild) {
... (truncated)