CVE-2026-106424
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifservices/audio/input_glitch_counter.cc |
modified | |
ifservices/audio/output_controller.cc |
modified | |
ifservices/audio/output_stream.cc |
modified |
Files Changed
services/audio/loopback_reference_manager.hservices/audio/input_sync_writer.ccservices/audio/output_stream.ccservices/audio/input_glitch_counter.ccservices/audio/output_controller.ccservices/audio/output_stream.hservices/audio/loopback_reference_manager.ccservices/audio/output_controller.hservices/audio/input_sync_writer.hservices/audio/input_glitch_counter.h
Patch
From 552949a94f798f24f75c894d44e6196cf4307bf5 Mon Sep 17 00:00:00 2001 From: henrika <[email protected]> Date: Mon, 18 May 2026 04:16:40 -0700 Subject: [PATCH] audio: Fix ASLR bypass by replacing raw pointer logging with tokens Several classes in services/audio were logging raw heap pointers (e.g.,'this') in diagnostic messages, which could be used to bypass ASLR. This CL replaces these raw pointer logs with base::UnguessableToken identifiers, following the pattern established in services/audio/input_stream.cc. Affected classes: - OutputController - OutputStream - InputSyncWriter - InputGlitchCounter - LoopbackReferenceManagerCore Bug: 513365978 Change-Id: Iad47193fc68e971d10a9ba3652aacefd88a6e70c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7852376 Commit-Queue: Henrik Andreasson <[email protected]> Reviewed-by: Olga Sharonova <[email protected]> Cr-Commit-Position: refs/heads/main@{#1632075} --- diff --git a/services/audio/input_glitch_counter.cc b/services/audio/input_glitch_counter.cc index c6002e9..f93fddc 100644 --- a/services/audio/input_glitch_counter.cc +++ b/services/audio/input_glitch_counter.cc @@ -29,7 +29,8 @@ InputGlitchCounter::InputGlitchCounter( base::RepeatingCallback<void(const std::string&)> log_callback) - : log_callback_(std::move(log_callback)) { + : id_(base::UnguessableToken::Create()), + log_callback_(std::move(log_callback)) { // Reserve one minutes worth of complete samples (assuming 10ms buffers) to // hopefully avoid allocating them on the realtime thread. complete_samples_.reserve(6); @@ -61,9 +62,9 @@ "AISW::%s => (number of detected audio glitches: %" PRIu64 " out of %" PRIu64 ") " - " [this=0x%" PRIXPTR "]", + " [id=%s]", __func__, global_sample_.dropped_data_count_, write_count_, - reinterpret_cast<uintptr_t>(this)); + id_.ToString().c_str()); log_callback_.Run(log_string); if (write_count_ < kSampleInterval) { diff --git a/services/audio/input_glitch_counter.h b/services/audio/input_glitch_counter.h index 60eb12d..f9b81ba6 100644 --- a/services/audio/input_glitch_counter.h +++ b/services/audio/input_glitch_counter.h @@ -7,8 +7,10 @@ #include <cstddef> #include <vector> + #include "base/functional/callback.h" #include "base/time/time.h" +#include "base/unguessable_token.h" namespace audio { // Class for logging audio glitch data at 10 second intervals, for use by @@ -68,6 +70,8 @@ size_t trailing_dropped_data_count_ = 0; } global_sample_; + const base::UnguessableToken id_; + base::RepeatingCallback<void(const std::string&)> log_callback_; // The number of calls to ReportMissedReadDeadline(), which corresponds to diff --git a/services/audio/input_sync_writer.cc b/services/audio/input_sync_writer.cc index 02865062..c7d73815 100644 --- a/services/audio/input_sync_writer.cc +++ b/services/audio/input_sync_writer.cc @@ -63,7 +63,8 @@ uint32_t shared_memory_segment_count, const media::AudioParameters& params, std::unique_ptr<InputGlitchCounter> glitch_counter) - : log_callback_(std::move(log_callback)), + : id_(base::UnguessableToken::Create()), + log_callback_(std::move(log_callback)), socket_(std::move(socket)), shared_memory_region_(std::move(shared_memory)), shared_memory_mapping_(shared_memory_region_.Map()), @@ -406,8 +407,7 @@ va_start(args, format); log_callback_.Run( base::StrCat({"AISW::", UNSAFE_TODO(base::StringPrintV(format, args)), - base::StringPrintf(" [this=0x%" PRIXPTR "]", - reinterpret_cast<uintptr_t>(this))})); + base::StringPrintf(" [id=%s]", id_.ToString().c_str())})); va_end(args); } diff --git a/services/audio/input_sync_writer.h b/services/audio/input_sync_writer.h index 2ccd796..e91c173 100644 --- a/services/audio/input_sync_writer.h +++ b/services/audio/input_sync_writer.h @@ -15,6 +15,7 @@ #include "base/memory/unsafe_shared_memory_region.h" #include "base/sync_socket.h" #include "base/time/time.h" +#include "base/unguessable_token.h" #include "build/build_config.h" #include "input_glitch_counter.h" #include "media/base/audio_bus.h" @@ -108,6 +109,8 @@ // Helper method for creating internal log messages prefixed with "AISW::". PRINTF_FORMAT(2, 3) void SendLogMessage(const char* format, ...); + const base::UnguessableToken id_; + const base::RepeatingCallback<void(const std::string&)> log_callback_; // Socket used to signal that audio data is ready. diff --git a/services/audio/loopback_reference_manager.cc b/services/audio/loopback_reference_manager.cc index 3510ed88..a455f6e 100644 --- a/services/audio/loopback_reference_manager.cc +++ b/services/audio/loopback_reference_manager.cc @@ -110,7 +110,8 @@ media::AudioManager* audio_manager, LoopbackReferenceStreamIdProvider* stream_id_provider, ErrorCallback on_error_callback) - : audio_manager_(audio_manager), + : id_(base::UnguessableToken::Create()), + audio_manager_(audio_manager), glitch_reporter_( media::SystemGlitchReporter::StreamType::kLoopbackReference), task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), @@ -141,8 +142,8 @@ } audio_log_->OnLogMessage(base::StringPrintf( - "LRMC::%s [id=%u] [this=0x%" PRIXPTR "]", message.c_str(), - stream_id_provider_->GetId(), reinterpret_cast<uintptr_t>(this))); + "LRMC::%s [stream_id=%u] [id=%s]", message.c_str(), + stream_id_provider_->GetId(), id_.ToString().c_str())); } void ReportAndResetGlitchStats() { @@ -287,6 +288,7 @@ audio_log_.reset(); } + const base::UnguessableToken id_; const raw_ptr<media::AudioManager> audio_manager_; media::SystemGlitchReporter glitch_reporter_; const scoped_refptr<base::SequencedTaskRunner> task_runner_; diff --git a/services/audio/loopback_reference_manager.h b/services/audio/loopback_reference_manager.h index 2dd37c6..30f3c9d6 100644 --- a/services/audio/loopback_reference_manager.h +++ b/services/audio/loopback_reference_manager.h @@ -10,6 +10,7 @@ #include "base/memory/raw_ptr.h" #include "base/sequence_checker.h" +#include "base/unguessable_token.h" #include "media/audio/audio_manager.h" #include "services/audio/reference_signal_provider.h" diff --git a/services/audio/output_controller.cc b/services/audio/output_controller.cc index 5bd7433..2a254ef 100644 --- a/services/audio/output_controller.cc +++ b/services/audio/output_controller.cc @@ -167,7 +167,8 @@ SyncReader* sync_reader, ManagedDeviceOutputStreamCreateCallback managed_device_output_stream_create_callback) - : audio_manager_(audio_manager), + : id_(base::UnguessableToken::Create()), + audio_manager_(audio_manager), params_(params), managed_device_output_stream_create_callback_( std::move(managed_device_output_stream_create_callback)), @@ -534,11 +535,11 @@ } void OutputController::SendLogMessage(const std::string& message) { - if (!handler_) + if (!handler_) { return; - handler_->OnLog(base::StringPrintf("AOC::%s [this=0x%" PRIXPTR "]", - message.c_str(), - reinterpret_cast<uintptr_t>(this))); + } + handler_->OnLog(base::StringPrintf("AOC::%s [id=%s]", message.c_str(), + id_.ToString().c_str())); } void OutputController::LogAudioPowerLevel(const char* call_name) { diff --git a/services/audio/output_controller.h b/services/audio/output_controller.h index a926070..21a7e2e 100644 --- a/services/audio/output_controller.h +++ b/services/audio/output_controller.h @@ -142,6 +142,8 @@ ~OutputController() override; + const base::UnguessableToken& id() const { return id_; } + // Methods to control playback of the stream. // Creates the audio output stream. This must be called before Play(). Returns @@ -300,6 +302,8 @@ // being called. void ProcessDeviceChange(); + const base::UnguessableToken id_; + const raw_ptr<media::AudioManager> audio_manager_; const media::AudioParameters params_; diff --git a/services/audio/output_stream.cc b/services/audio/output_stream.cc index c857c84..85ec7e0 100644 --- a/services/audio/output_stream.cc +++ b/services/audio/output_stream.cc @@ -175,7 +175,8 @@ const media::AudioParameters& params, LoopbackCoordinator* coordinator, const base::UnguessableToken& loopback_group_id) - : delete_callback_(std::move(delete_callback)), + : id_(base::UnguessableToken::Create()), + delete_callback_(std::move(delete_callback)), receiver_(this, std::move(stream_receiver)), device_switch_receiver_(this, std::move(device_switch_receiver)), observer_(std::move(observer)), @@ -464,12 +465,12 @@ } void OutputStream::SendLogMessage(const std::string& message) { - if (!log_) + if (!log_) { return; - log_->OnLogMessage( - "audio::OS::" + message + - base::StringPrintf(" [controller=0x%" PRIXPTR "]", - reinterpret_cast<uintptr_t>(&controller_))); + } + log_->OnLogMessage(base::StringPrintf( + "audio::OS::%s [id=%s] [controller_id=%s]", message.c_str(), + id_.ToString().c_str(), controller_.id().ToString().c_str())); } // Static diff --git a/services/audio/output_stream.h b/services/audio/output_stream.h index 6a56b089..4d30a6dc 100644 --- a/services/audio/output_stream.h +++ b/services/audio/output_stream.h @@ -15,6 +15,7 @@ #include "base/sequence_checker.h" #include "base/sync_socket.h" #include "base/timer/timer.h" +#include "base/unguessable_token.h" #include "media/mojo/mojom/audio_data_pipe.mojom.h" #include "media/mojo/mojom/audio_logging.mojom.h" #include "media/mojo/mojom/audio_output_stream.mojom.h" @@ -124,6 +125,8 @@ SEQUENCE_CHECKER(owning_sequence_); + const base::UnguessableToken id_; + base::CancelableSyncSocket foreign_socket_; DeleteCallback delete_callback_; mojo::Receiver<AudioOutputStream> receiver_;
Original Bug Report
ASLR bypass via raw pointer leakage in audio service logs
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 without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: Multiple audio service components log raw heap pointers which are broadcast to renderers via WebRTC diagnostic logs. On Android and ChromeOS, where the audio service runs in the browser process, this allows a compromised renderer with specific permissions to obtain browser-process addresses and bypass ASLR.
Affected files:
services/audio/output_controller.ccservices/audio/output_stream.ccservices/audio/input_sync_writer.ccservices/audio/input_glitch_counter.ccservices/audio/loopback_reference_manager.cc
Estimated timestamp from git blame: 2021-04-01
Summary
Multiple classes within the audio service include raw heap pointers in their diagnostic log messages. These logs are transmitted via Mojo to the browser process, where they are broadcast to all renderers that have registered a WebRTC log callback. On platforms such as Android and ChromeOS, where the audio service runs within the browser process, this leakage allows a compromised renderer to obtain live browser-process heap addresses, providing a deterministic primitive for bypassing ASLR.
Root Cause Analysis
Several components in services/audio/ explicitly log the this pointer or other object addresses using the %PRIXPTR format specifier. These logs are intended for field debugging but fail to mask sensitive memory locations.
Key occurrences include:
services/audio/output_controller.cc:OutputController::SendLogMessagelogs[this=0x... ](line 539).services/audio/output_stream.cc:OutputStream::SendLogMessagelogs the address of itscontroller_member (line 471).services/audio/input_sync_writer.cc:InputSyncWriter::SendLogMessagelogs[this=0x... ](line 409).services/audio/input_glitch_counter.cc:InputGlitchCounter::Loglogs[this=0x... ](line 64).services/audio/loopback_reference_manager.cc:LoopbackReferenceManager::SendLogMessagelogs[this=0x... ](line 144).
Vulnerability Path
- Logging: An audio service component generates a log message containing a raw pointer.
- Mojo Transmission: The message is sent through the
media::mojom::AudioLoginterface toMediaInternals::AudioLogImplin the browser process. - Broadcast:
MediaInternalsforwards the message toMediaStreamManager::SendMessageToNativeLog, which broadcasts it to all callbacks registered inlog_callbacks_on the IO thread without verifying the origin or the target renderer (content/browser/renderer_host/media/media_stream_manager.cc:3537). - Renderer Access: A compromised renderer belonging to an extension with the
webrtcLoggingPrivatepermission (such as the Hangouts component extension) can register a log callback. It can then retrieve these logs—including the leaked pointers—via thechrome.webrtcLoggingPrivate.storeandgetLogsDirectoryAPIs.
Impact
This vulnerability provides a reliable primitive to bypass ASLR:
- Android and ChromeOS: The audio service runs in the browser process by default (
kAudioServiceOutOfProcessis disabled). The leaked pointers are live browser-process PartitionAlloc heap addresses. - Linux: The audio service runs out-of-process but is typically unsandboxed, leading to an ASLR bypass for that process.
Potential Steps to Reproduce
- Compromise a renderer process that has access to the
webrtcLoggingPrivateAPI (e.g., via the Hangouts extension). - Invoke
chrome.webrtcLoggingPrivate.start()to begin collecting diagnostic logs. - Trigger audio activity (e.g., play a video or trigger a system notification) to generate audio service logs.
- Invoke
chrome.webrtcLoggingPrivate.store()followed bychrome.webrtcLoggingPrivate.getLogsDirectory()to access the log files. - Read and parse the log files (e.g., looking for the
AOC::orAISW::prefixes) to extract hex-encoded heap addresses.
Recommended Fix
Replace the use of raw pointers in log messages with non-sensitive unique identifiers, such as base::UnguessableToken, for diagnostic correlation. This pattern is already correctly implemented in services/audio/input_stream.cc.
Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e
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.