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 <omerkatz@chromium.org>
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 <emaxx@google.com>
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
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