CVE-2026-14406
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/profiler/profile-generator.cc |
modified |
Files Changed
src/profiler/profile-generator.cc
Patch
From 6d33b8b329ef903dde6a296ebd973a41ea298de6 Mon Sep 17 00:00:00 2001
From: Benedikt Meurer <bmeurer@chromium.org>
Date: Fri, 15 May 2026 13:06:18 +0200
Subject: [PATCH] [profiler] Prevent data race on `CodeEntry::rare_data_`.
This adds an explicit memory barrier to `CodeEntry::EnsureRareData()`
for platforms with store-store reordering (e.g. arm64), where it's
otherwise possible to that the store to `rare_data_` becomes visible to
other threads before the field initialization completes.
Fixed: 513435594
Change-Id: I78a45c35bed6a3a2459c1ec994393709027c5f22
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7849376
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#107368}
---
diff --git a/src/profiler/profile-generator.cc b/src/profiler/profile-generator.cc
index b62c067..bb7aea1 100644
--- a/src/profiler/profile-generator.cc
+++ b/src/profiler/profile-generator.cc
@@ -5,6 +5,7 @@
#include "src/profiler/profile-generator.h"
#include <algorithm>
+#include <atomic>
#include <vector>
#include "include/v8-profiler.h"
@@ -270,7 +271,15 @@
CodeEntry::RareData* CodeEntry::EnsureRareData() {
if (!rare_data_) {
- rare_data_.reset(new RareData());
+ // On platforms with store-store reordering, we need to ensure that the
+ // field initializations in RareData complete before the pointer (in the
+ // std::unique_ptr) becomes visible. Using an std::atomic for rare_data_
+ // would also work, but would penalize every read, also on platforms with
+ // no store-store reordering (like x64).
+ // See http://crbug.com/513435594 for additional details.
+ RareData* rare_data = new RareData();
+ std::atomic_thread_fence(std::memory_order_release);
+ rare_data_.reset(rare_data);
}
return rare_data_.get();
}
Original Bug Report
Potential data race in V8 CPU Profiler allows arbitrary memory read via CDP
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: A data race in V8’s CPU profiler occurs when CodeEntry::rare_data_ is accessed concurrently by the profiler and main threads without synchronization. On weakly-ordered architectures like ARM64, this can lead to an arbitrary native memory read primitive accessible via the Chrome DevTools Protocol (CDP).
Affected files:
v8/src/profiler/profile-generator.hv8/src/profiler/profile-generator.ccv8/src/profiler/cpu-profiler.ccv8/src/inspector/v8-profiler-agent-impl.ccv8/src/api/api.ccv8/src/profiler/cpu-profiler-inl.h
Estimated timestamp from git blame: 2018-05-18
Summary
A potential data race in the V8 CPU profiler allows for a wild pointer read, which could be exploited to leak renderer process memory via the Chrome DevTools Protocol (CDP). The issue stems from unsynchronized access to CodeEntry::rare_data_ when a profile is being serialized on the main thread while the profiler background thread is concurrently updating deoptimization or bailout metadata.
Technical Details
The CodeEntry class (defined in v8/src/profiler/profile-generator.h) uses a std::unique_ptr<RareData> rare_data_ to store optional metadata such as bailout reasons and deoptimization information. This pointer is accessed across threads without atomic protections or mutexes.
-
Concurrent Writer: The
SamplingEventsProcessorthread processes code events (e.g.,CodeDisableOptEvent). It callsCodeEntry::set_bailout_reason(), which invokesEnsureRareData(). This method allocates a newRareDataobject and assigns it to therare_data_unique pointer:CodeEntry::RareData* CodeEntry::EnsureRareData() { if (!rare_data_) { rare_data_.reset(new RareData()); } return rare_data_.get(); } -
Concurrent Reader: When multiple profiling sessions are active on a single
v8::CpuProfiler, callingStopProfilingon one session does not stop the background profiler thread if other sessions remain active. While the background thread continues processing events, the main thread begins serializing the stopped profile. This eventually callsCodeEntry::bailout_reason()via the V8 API (v8::CpuProfileNode::GetBailoutReason):const char* bailout_reason() const { return rare_data_ ? rare_data_->bailout_reason_ : kEmptyBailoutReason; } -
The Race: On weakly-ordered architectures (such as ARM64), the memory store to
rare_data_and the initialization of its fields (likebailout_reason_) can be reordered. Consequently, the main thread may observe a non-nullrare_data_pointer before its internalbailout_reason_pointer has been correctly initialized. If the underlying memory was previously controlled by an attacker (e.g., via heap spraying), the reader may dereference a stale or malicious pointer.
Potential Impact
An attacker with access to the Profiler CDP domain (e.g., via a chrome.debugger extension or a compromised renderer) could potentially perform an arbitrary-address null-terminated read of the renderer process memory. The leaked data is returned to the CDP client as a string in the consoleProfileFinished event or the Profiler.stop response. This could be used to leak sensitive information or bypass ASLR.
Suggested Potential Exploitation Steps
- Attach a CDP client to the target renderer.
- Spray the native heap with objects of the same size as
RareDatacontaining a target memory address at the offset ofbailout_reason_. - Start multiple concurrent CPU profiling sessions.
- Trigger a code event that causes an optimization bailout (e.g., executing code that violates a previous optimization assumption).
- Immediately stop one of the profiling sessions.
- The race condition may cause the serialization process to read the attacker-controlled pointer as a
const char*, leaking memory from that address into the CDP response.
Suggested Fix
Access to CodeEntry::rare_data_ should be synchronized. This can be achieved by making rare_data_ an std::atomic<RareData*> with proper memory ordering (Acquire/Release) or by protecting the CodeEntry metadata with a mutex. Given that CodeEntry objects are heavily used during profiling, a lock-free approach with atomics is likely preferable for performance.
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.