CVE-2026-10965
Overview
Files Changed
src/profiler/profile-generator.h
Patch
From 5472defe56a3100383935e051f87cf2eb338169d Mon Sep 17 00:00:00 2001 From: Benedikt Meurer <[email protected]> Date: Tue, 12 May 2026 13:23:36 +0200 Subject: [PATCH] [profiler] Fix race condition in code entry refcounting. Fixed: 511290038 Change-Id: I16f1d52578fa8d85d2552ee130f468efbae44e0e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7837982 Reviewed-by: Philip Pfaffe <[email protected]> Auto-Submit: Benedikt Meurer <[email protected]> Commit-Queue: Benedikt Meurer <[email protected]> Cr-Commit-Position: refs/heads/main@{#107260} --- diff --git a/src/profiler/profile-generator.h b/src/profiler/profile-generator.h index 38b3c71..5834520 100644 --- a/src/profiler/profile-generator.h +++ b/src/profiler/profile-generator.h @@ -230,15 +230,13 @@ size_t AddRef() { DCHECK(is_ref_counted()); DCHECK_LT(ref_count_, std::numeric_limits<size_t>::max()); - ref_count_++; - return ref_count_; + return ++ref_count_; } size_t DecRef() { DCHECK(is_ref_counted()); DCHECK_GT(ref_count_, 0UL); - ref_count_--; - return ref_count_; + return --ref_count_; } using EventField = base::BitField<LogEventListener::Event, 0, 4>;
Original Bug Report
Double-Free in V8 CPU Profiler via CodeEntry::DecRef Race Condition
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: A race condition exists in V8’s CPU Profiler where CodeEntry::DecRef implements reference counting unsafely using separate atomic decrement and load operations. This allows two concurrent threads to both observe a reference count of 0, bypassing single-deletion checks and leading to a potential Double-Free and Use-After-Free. The issue is reachable from web content via the JS Self-Profiling API.
Affected files:
v8/src/profiler/profile-generator.hv8/src/profiler/profile-generator.ccv8/src/profiler/cpu-profiler.ccthird_party/blink/renderer/core/timing/profiler_group.cc
Estimated timestamp from git blame: 2021-07-02
Summary
A race condition in the V8 CPU Profiler’s CodeEntry reference counting logic can potentially lead to a Use-After-Free (UAF) and Double-Free. The CodeEntry::DecRef function uses a non-atomic sequence of operations to decrement and evaluate the reference count. When two threads concurrently decrement a CodeEntry with a reference count of 2, both can observe the resulting count as 0, causing both threads to attempt to free the object.
Technical Details
In v8/src/profiler/profile-generator.h, the CodeEntry::DecRef function is implemented as follows:
size_t DecRef() {
DCHECK(is_ref_counted());
DCHECK_GT(ref_count_, 0UL);
ref_count_--;
return ref_count_;
}
Although ref_count_ is an std::atomic<std::size_t>, the postfix decrement (ref_count_--) and the subsequent load (return ref_count_;) are two distinct atomic operations. This introduces a race condition. If a CodeEntry has a reference count of 2, and two threads invoke DecRef concurrently, the following interleaving can occur:
- Thread A executes
ref_count_--(count becomes 1). - Thread B executes
ref_count_--(count becomes 0). - Thread A executes
return ref_count_;(returns 0). - Thread B executes
return ref_count_;(returns 0).
When CodeEntryStorage::DecRef (in v8/src/profiler/profile-generator.cc) receives the return value, it checks if (entry->is_ref_counted() && entry->DecRef() == 0). Because both threads received 0, both will enter the block and execute delete entry; (and potentially iterate over entry->rare_data_), leading to a double-free and UAF.
Suggested Steps to Trigger (Theoretical)
Note: These are potential steps as our tooling agent cannot execute code to verify a working exploit.
- Enable API: The attacker serves a page with the
Document-Policy: js-profilingHTTP header to enable the JS Self-Profiling API. - Create Profilers: The attacker’s JavaScript creates multiple concurrent profilers (e.g.,
p1andp2). This starts V8’s backgroundSamplingEventsProcessorthread. - Generate Events: The JS triggers heavy JIT compilation and subsequent Garbage Collection to generate
kCodeDeleteevents, which are queued to the background thread. - Stop One Profiler: The attacker stops
p1(p1.stop()) and drops references to it. Becausep2is still running, theSamplingEventsProcessorbackground thread continues executing. - Trigger Cleanup: Blink’s garbage collection cleans up
p1, posting an asynchronous task to the main thread to delete theCpuProfile. - The Race: On the main thread, the destruction of
ProfileNodes callsDecRefonCodeEntryobjects. Concurrently, the background thread dequeueskCodeDeleteevents and callsInstructionStreamMap::RemoveCode, which also callsDecRefon the sameCodeEntryobjects. If timed correctly, the race condition occurs.
Exploitability
CodeEntry is a standard C++ object allocated on the heap. Pointers to it (such as those in ProfileNode and InstructionStreamMap) are held as raw C++ pointers (CodeEntry*), rather than being protected by base::raw_ptr. Therefore, this vulnerability is not mitigated by MiraclePtr (BackupRefPtr) and can potentially be leveraged for Remote Code Execution (RCE) in the Renderer process.
Suggested Fix
Refactor CodeEntry::DecRef (and similarly AddRef) to use a single atomic operation that evaluates and returns the new value. For example:
size_t DecRef() {
DCHECK(is_ref_counted());
DCHECK_GT(ref_count_, 0UL);
return --ref_count_; // Prefix decrement ensures a single atomic instruction
}
Alternatively, return ref_count_.fetch_sub(1) - 1; can be used to ensure the returned value precisely reflects the decrement operation performed by the calling thread.
Evaluated with Chrome root at commit: eca8648a4e1cdfdda68c495a6003059fed641955
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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.