Chrome · GarbageCollection
CVE-2026-119263
Memory Corruption in GarbageCollection
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/base/synchronization-point-support.cc |
modified | |
whilesrc/base/synchronization-point-support.cc |
modified | |
V8_BASE_EXPORTsrc/base/synchronization-point-support.h |
modified | |
ifsrc/common/synchronization-point-support.cc |
modified | |
whilesrc/common/synchronization-point-support.cc |
modified |
Files Changed
src/maglev/maglev-compiler.ccsrc/heap/factory-base.ccsrc/heap/array-buffer-sweeper.ccsrc/handles/persistent-handles.ccsrc/runtime/runtime-typedarray.ccBUILD.bazelsrc/heap/cppgc-internal/platform.cctest/unittests/api/deserialize-unittest.ccsrc/objects/string.ccsrc/compiler/pipeline.ccsrc/compiler/turboshaft/pipelines.hsrc/runtime/runtime-test.ccsrc/maglev/maglev-concurrent-dispatcher.ccBUILD.gnsrc/extensions/externalize-string-extension.cctest/unittests/heap/cppgc/concurrent-marking-unittest.ccsrc/heap/concurrent-marking.ccsrc/regexp/regexp.ccsrc/common/synchronization-point-support.ccsrc/compiler/common-utils.ccsrc/objects/shared-function-info-inl.hsrc/base/synchronization-point-support.ccsrc/heap/factory.ccsrc/init/v8.ccsrc/base/synchronization-point-support.hsrc/compiler/turboshaft/turbolev-frontend-pipeline.ccsrc/heap/sweeper.cc
Patch
From 1abb88d710384fb3eb288ec56cac399a9dc57fe8 Mon Sep 17 00:00:00 2001 From: Omer Katz <[email protected]> Date: Tue, 27 Oct 2026 16:11:10 +0100 Subject: [PATCH] [base, cppgc] Support synchronization points in standalone cppgc Decouple SynchronizationPointSupport from V8 internals and move it to v8_libbase so that synchronization points can be used in standalone cppgc builds and tests. Bug: 575143870 Change-Id: Ie917eda7de79a019e10aeb80b03b7ce145a91d2e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8656862 Reviewed-by: Maksim Ivanov <[email protected]> Commit-Queue: Omer Katz <[email protected]> Reviewed-by: Michael Lippautz <[email protected]> Cr-Commit-Position: refs/heads/main@{#111258} --- diff --git a/src/base/synchronization-point-support.cc b/src/base/synchronization-point-support.cc new file mode 100644 index 0000000..8d97cb9 --- /dev/null +++ b/src/base/synchronization-point-support.cc @@ -0,0 +1,126 @@ +// Copyright 2026 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/base/synchronization-point-support.h" + +#include <inttypes.h> + +#include <atomic> +#include <memory> +#include <string> +#include <string_view> +#include <tuple> +#include <utility> + +#include "src/base/platform/mutex.h" +#include "src/base/platform/platform.h" +#include "src/base/platform/time.h" +#include "src/base/thread-id.h" + +namespace v8 { +namespace base { + +SynchronizationPointSupport* SynchronizationPointSupport::instance_ = nullptr; + +void SynchronizationPointSupport::Initialize() { + if (!instance_) { + instance_ = new SynchronizationPointSupport(); + } +} + +void SynchronizationPointSupport::RequestBlockAt( + std::string synchronization_point, TimeDelta timeout) { + any_requested_ = true; + MutexGuard lock(&mutex_); + std::unique_ptr<SyncPointState>& state = + states_[std::move(synchronization_point)]; + if (!state) state = std::make_unique<SyncPointState>(); + state->block_requested = true; + state->block_requester_thread = ThreadId::Current(); + state->block_timeout = timeout; +} + +bool SynchronizationPointSupport::Resume( + std::string_view synchronization_point) { + MutexGuard lock(&mutex_); + auto it = states_.find(synchronization_point); + if (it != states_.end()) { + SyncPointState* state = it->second.get(); + if (state->block_requested) { + state->block_requested = false; + state->cv.NotifyAll(); + return true; + } + } + OS::PrintError("Synchronization point not armed\n"); + return false; +} + +bool SynchronizationPointSupport::WaitUntilBlocked( + std::string_view synchronization_point, TimeDelta timeout) { + MutexGuard lock(&mutex_); + auto it = states_.find(synchronization_point); + if (it == states_.end()) { + OS::PrintError("Synchronization point not armed\n"); + return false; + } + SyncPointState* state = it->second.get(); + + const TimeTicks start = TimeTicks::Now(); + while (state->blocked_threads == 0) { + if (!state->block_requested) { + OS::PrintError("Synchronization point not armed\n"); + return false; + } + const TimeDelta remaining = start + timeout - TimeTicks::Now(); + if (remaining <= TimeDelta()) { + OS::PrintError("Synchronization point wait timed out\n"); + return false; + } + std::ignore = state->cv.WaitFor(&mutex_, remaining); + } + return true; +} + +void SynchronizationPointSupport::BlockIfRequestedSlow( + std::string_view synchronization_point) { + // Safe: map elements are never removed, so the pointer is stable. + SyncPointState* state = nullptr; + { + MutexGuard lock(&mutex_); + auto it = states_.find(synchronization_point); + if (it == states_.end()) return; + state = it->second.get(); + if (!state->block_requested) return; + if (state->block_requester_thread == ThreadId::Current()) { + OS::PrintError( + "Warning: ignoring self-deadlock at synchronization point '%.*s'\n", + static_cast<int>(synchronization_point.length()), + synchronization_point.data()); + return; + } + } + + TimeTicks start = TimeTicks::Now(); + MutexGuard lock(&mutex_); + ++state->blocked_threads; + state->cv.NotifyAll(); + while (state->block_requested) { + TimeDelta remaining = start + state->block_timeout - TimeTicks::Now(); + if (remaining <= TimeDelta()) break; + std::ignore = state->cv.WaitFor(&mutex_, remaining); + } + --state->blocked_threads; + + if (state->block_requested) { + OS::PrintError( + "Warning: Synchronization point '%.*s' timed out after %" PRId64 + " ms. Resuming automatically.\n", + static_cast<int>(synchronization_point.length()), + synchronization_point.data(), state->block_timeout.InMilliseconds()); + } +} + +} // namespace base +} // namespace v8 diff --git a/src/base/synchronization-point-support.h b/src/base/synchronization-point-support.h new file mode 100644 index 0000000..88617e4 --- /dev/null +++ b/src/base/synchronization-point-support.h @@ -0,0 +1,103 @@ +// Copyright 2026 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_BASE_SYNCHRONIZATION_POINT_SUPPORT_H_ +#define V8_BASE_SYNCHRONIZATION_POINT_SUPPORT_H_ + +#include <atomic> +#include <functional> +#include <map> +#include <memory> +#include <string> +#include <string_view> + +#include "include/v8config.h" +#include "src/base/base-export.h" +#include "src/base/macros.h" +#include "src/base/platform/condition-variable.h" +#include "src/base/platform/mutex.h" +#include "src/base/platform/time.h" +#include "src/base/thread-id.h" + +namespace v8 { +namespace base { + +// Singleton that backs up the synchronization point functionality, which allows +// blocking the V8 execution at potentially interesting places in tests/fuzzers +// to simulate specific timings. +// +// The C++ code should use the `SYNCHRONIZATION_POINT` macro in order to let the +// execution there be controlled via the %BlockAt/%Resume/%WaitUntilBlocked +// native APIs. +class V8_BASE_EXPORT SynchronizationPointSupport { + public: + static void Initialize(); + static V8_INLINE SynchronizationPointSupport* Get() { return instance_; } + + // Requests a block at the given synchronization point. When a thread reaches + // it, it will block for the specified timeout (or less, if it is resumed). + void RequestBlockAt(std::string synchronization_point, TimeDelta timeout); + + // Resumes a thread currently blocked at the given synchronization point. + // Returns false if a block wasn't requested. + bool Resume(std::string_view synchronization_point); + + // Waits until the given synchronization point is reached by some thread. + // Returns false if a block wasn't requested or on timeout. Note: this does + // not request a block; it must be requested first. + bool WaitUntilBlocked(std::string_view synchronization_point, + TimeDelta timeout); + + // Called when the synchronization point is reached; blocks if requested. + V8_INLINE void BlockIfRequested(std::string_view synchronization_point) { + if (!any_requested_.load(std::memory_order_relaxed)) [[likely]] { + return; + } + BlockIfRequestedSlow(synchronization_point); + } + + private: + struct SyncPointState { + ConditionVariable cv; + // Set to true to signal that any thread that reaches this point should + // block. + bool block_requested = false; + // Number of threads that have reached the point and are currently blocked. + int blocked_threads = 0; + // The identity of the thread that set the `block_requested` flag. + ThreadId block_requester_thread = ThreadId::Invalid(); + // How long a thread should remain blocked, unless resumed. + TimeDelta block_timeout; + }; + + V8_NOINLINE V8_PRESERVE_MOST void BlockIfRequestedSlow( + std::string_view synchronization_point); + + static SynchronizationPointSupport* instance_; + + std::atomic<bool> any_requested_{false}; + Mutex mutex_; + std::map<std::string, std::unique_ptr<SyncPointState>, std::less<>> states_; +}; + +// A synchronization point that allows a thread reaching it to be predictably +// blocked and resumed by JS testing intrinsics (%BlockAt and %Resume). +#define SYNCHRONIZATION_POINT(sync_point_name) \ + v8::base::SynchronizationPointSupport::Get()->BlockIfRequested( \ + sync_point_name) + +// Similar to `SYNCHRONIZATION_POINT`, but is intended for being used on hot +// paths. Unlike the former, it compiles to nop unless the +// `v8_enable_test_only_sync_points` GN flag is true. +#ifdef V8_ENABLE_TEST_ONLY_SYNC_POINTS +#define SYNCHRONIZATION_POINT_TEST_ONLY(sync_point_name) \ + SYNCHRONIZATION_POINT(sync_point_name) +#else +#define SYNCHRONIZATION_POINT_TEST_ONLY(sync_point_name) ((void)0) +#endif + +} // namespace base +} // namespace v8 + +#endif // V8_BASE_SYNCHRONIZATION_POINT_SUPPORT_H_ diff --git a/src/common/synchronization-point-support.cc b/src/common/synchronization-point-support.cc deleted file mode 100644 index ffdeead..0000000 --- a/src/common/synchronization-point-support.cc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2026 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/common/synchronization-point-support.h" - -#include <inttypes.h> - -#include <atomic> -#include <memory> -#include <string> -#include <string_view> -#include <tuple> -#include <utility> - -#include "src/base/platform/mutex.h" -#include "src/base/platform/platform.h" -#include "src/base/platform/time.h" -#include "src/base/thread-id.h" -#include "src/heap/local-heap-inl.h" - -namespace v8 { -namespace internal { - -SynchronizationPointSupport* SynchronizationPointSupport::instance_ = nullptr; - -void SynchronizationPointSupport::Initialize() { - CHECK_NULL(instance_); - instance_ = new SynchronizationPointSupport(); -} - -void SynchronizationPointSupport::RequestBlockAt( - std::string synchronization_point, base::TimeDelta timeout) { - any_requested_ = true; - base::MutexGuard lock(&mutex_); - std::unique_ptr<SyncPointState>& state = - states_[std::move(synchronization_point)]; - if (!state) state = std::make_unique<SyncPointState>(); - state->block_requested = true; - state->block_requester_thread = base::ThreadId::Current(); - state->block_timeout = timeout; -} - -bool SynchronizationPointSupport::Resume( - std::string_view synchronization_point) { - base::MutexGuard lock(&mutex_); - auto it = states_.find(synchronization_point); - if (it != states_.end()) { - SyncPointState* state = it->second.get(); - if (state->block_requested) { - state->block_requested = false; - state->cv.NotifyAll(); - return true; - } - } - base::OS::PrintError("Synchronization point not armed\n"); - return false; -} - -bool SynchronizationPointSupport::WaitUntilBlocked( - std::string_view synchronization_point, base::TimeDelta timeout) { - // Safe: map elements are never removed, so the pointer is stable. - SyncPointState* state; - { - base::MutexGuard lock(&mutex_); - auto it = states_.find(synchronization_point); - if (it == states_.end()) { - base::OS::PrintError("Synchronization point not armed\n"); - return false; - } - state = it->second.get(); - } - - bool success = false; - auto wait_loop = [this, state, timeout, &success]() { - base::MutexGuard lock(&mutex_); - const base::TimeTicks start = base::TimeTicks::Now(); - while (state->blocked_threads == 0) { - if (!state->block_requested) { - base::OS::PrintError("Synchronization point not armed\n"); - return; - } - const base::TimeDelta remaining = - start + timeout - base::TimeTicks::Now(); - if (remaining <= base::TimeDelta()) { - base::OS::PrintError("Synchronization point wait timed out\n"); - return; - } - std::ignore = state->cv.WaitFor(&mutex_, remaining); - } - success = true; - }; - - LocalHeap* local_heap = LocalHeap::TryGetCurrent(); - if (local_heap && local_heap->IsRunning()) { - local_heap->ExecuteWhileParked(wait_loop); - } else { - wait_loop(); - } - return success; -} - -void SynchronizationPointSupport::BlockIfRequestedSlow( - std::string_view synchronization_point) { - // Safe: map elements are never removed, so the pointer is stable. - SyncPointState* state = nullptr; - { - base::MutexGuard lock(&mutex_); - auto it = states_.find(synchronization_point); - if (it == states_.end()) return; - state = it->second.get(); - if (!state->block_requested) return; - if (state->block_requester_thread == base::ThreadId::Current()) { - base::OS::PrintError( - "Warning: ignoring self-deadlock at synchronization point '%.*s'\n", - static_cast<int>(synchronization_point.length()), - synchronization_point.data()); - return; - } - } - - base::TimeTicks start = base::TimeTicks::Now(); - base::MutexGuard lock(&mutex_); - ++state->blocked_threads; - state->cv.NotifyAll(); - while (state->block_requested) { - base::TimeDelta remaining = - start + state->block_timeout - base::TimeTicks::Now(); - if (remaining <= base::TimeDelta()) break; - std::ignore = state->cv.WaitFor(&mutex_, remaining); - } - --state->blocked_threads; - - if (state->block_requested) { - base::OS::PrintError( - "Warning: Synchronization point '%.*s' timed out after %" PRId64 ... (file diff 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