High chrome Memory Corruption 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in GarbageCollection
DescriptionMemory corruption in GarbageCollection
ComponentGarbageCollection
Bug ClassMemory Corruption
Tracker575143870
Fix commit1abb88d71038 (v8/v8) +185/-203
CISA KEVNot listed
CreditedXinyang Ge
Disclosed2026-10-29

Changed Functions

FunctionChangeNotes
if
src/base/synchronization-point-support.cc
modified
while
src/base/synchronization-point-support.cc
modified
V8_BASE_EXPORT
src/base/synchronization-point-support.h
modified
if
src/common/synchronization-point-support.cc
modified
while
src/common/synchronization-point-support.cc
modified

Files Changed

  • src/maglev/maglev-compiler.cc
  • src/heap/factory-base.cc
  • src/heap/array-buffer-sweeper.cc
  • src/handles/persistent-handles.cc
  • src/runtime/runtime-typedarray.cc
  • BUILD.bazel
  • src/heap/cppgc-internal/platform.cc
  • test/unittests/api/deserialize-unittest.cc
  • src/objects/string.cc
  • src/compiler/pipeline.cc
  • src/compiler/turboshaft/pipelines.h
  • src/runtime/runtime-test.cc
  • src/maglev/maglev-concurrent-dispatcher.cc
  • BUILD.gn
  • src/extensions/externalize-string-extension.cc
  • test/unittests/heap/cppgc/concurrent-marking-unittest.cc
  • src/heap/concurrent-marking.cc
  • src/regexp/regexp.cc
  • src/common/synchronization-point-support.cc
  • src/compiler/common-utils.cc
  • src/objects/shared-function-info-inl.h
  • src/base/synchronization-point-support.cc
  • src/heap/factory.cc
  • src/init/v8.cc
  • src/base/synchronization-point-support.h
  • src/compiler/turboshaft/turbolev-frontend-pipeline.cc
  • src/heap/sweeper.cc
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.