Chrome · V8 › Maps
CVE-2026-118320
Memory Corruption in V8 (Maps)
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/objects/map-updater.cc |
modified | |
forsrc/objects/transitions.cc |
modified |
Files Changed
src/objects/map-updater.ccsrc/objects/transitions.ccsrc/objects/transitions.hsrc/objects/transitions-inl.h
Patch
From 5a4d7a78dbb4d7451e3925d6b3d1b68d261fad16 Mon Sep 17 00:00:00 2001
From: Igor Sheludko <ishell@chromium.org>
Date: Wed, 14 Oct 2026 15:37:46 +0200
Subject: [PATCH] [maps] Synchronize concurrent GetPrototypeTransition in TryUpdateNoLock
Make TransitionsAccessor::GetPrototypeTransition an instance method on
TransitionsAccessor and guard prototype transition cache lookups with
full_transition_array_access() when accessed concurrently from
MapUpdater::TryUpdateNoLock.
This pairs with the full_transition_array_access() lock already held in
TransitionsAccessor::PutPrototypeTransition during compaction and new
entry publication, preventing concurrent readers from observing
partially-initialized maps or in-progress array compactions.
Fixed: 568894515
TAG=agy
CONV=d848b9e2-cbea-4f48-a8e4-dec738f151e6
Change-Id: Ib04a65ac07c2c588cb9e3aecc412068e2902b9aa
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8576038
Commit-Queue: Jakob Linke <jgruber@chromium.org>
Auto-Submit: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Jakob Linke <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#110759}
---
diff --git a/src/objects/map-updater.cc b/src/objects/map-updater.cc
index d62c947..f143735 100644
--- a/src/objects/map-updater.cc
+++ b/src/objects/map-updater.cc
@@ -385,8 +385,9 @@
}
if (root_map->prototype() != old_map->prototype()) {
- auto maybe_transition = TransitionsAccessor::GetPrototypeTransition(
- isolate, root_map, old_map->prototype());
+ auto maybe_transition =
+ TransitionsAccessor(isolate, root_map, IsConcurrent(cmode))
+ .GetPrototypeTransition(old_map->prototype());
if (!maybe_transition) {
return {};
}
diff --git a/src/objects/transitions-inl.h b/src/objects/transitions-inl.h
index af50967..b694790 100644
--- a/src/objects/transitions-inl.h
+++ b/src/objects/transitions-inl.h
@@ -400,6 +400,14 @@
return MaybeHandle<Map>(result, isolate);
}
+// static
+std::optional<Tagged<Map>> TransitionsAccessor::GetPrototypeTransition(
+ Isolate* isolate, Tagged<Map> map, Tagged<Object> prototype) {
+ // This helper is supposed to be used only from the main thread.
+ DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
+ return TransitionsAccessor(isolate, map).GetPrototypeTransition(prototype);
+}
+
// TODO(375937549): Convert to uint32_t.
int TransitionArray::number_of_transitions() const {
if (ulength().value() < kFirstIndex) return 0;
diff --git a/src/objects/transitions.cc b/src/objects/transitions.cc
index bcadaa3..f20dde6 100644
--- a/src/objects/transitions.cc
+++ b/src/objects/transitions.cc
@@ -434,8 +434,8 @@
const uint32_t header = TransitionArray::kProtoTransitionHeaderSize;
- DirectHandle<WeakFixedArray> cache(GetPrototypeTransitions(isolate, *map),
- isolate);
+ DirectHandle<WeakFixedArray> cache(
+ TransitionsAccessor(isolate, *map).GetPrototypeTransitions(), isolate);
const uint32_t cache_len = cache->ulength().value();
const uint32_t capacity = cache_len >= header ? cache_len - header : 0;
const uint32_t transitions =
@@ -476,11 +476,13 @@
return true;
}
-// static
std::optional<Tagged<Map>> TransitionsAccessor::GetPrototypeTransition(
- Isolate* isolate, Tagged<Map> map, Tagged<Object> prototype) {
+ Tagged<Object> prototype) {
DisallowGarbageCollection no_gc;
- Tagged<WeakFixedArray> cache = GetPrototypeTransitions(isolate, map);
+ if (encoding() != kFullTransitionArray) return {};
+ base::MutexGuardIf guard(isolate_->full_transition_array_access(),
+ concurrent_access_);
+ Tagged<WeakFixedArray> cache = GetPrototypeTransitions();
const uint32_t length = TransitionArray::NumberOfPrototypeTransitions(cache);
for (uint32_t i = 0; i < length; i++) {
Tagged<MaybeObject> target =
@@ -497,19 +499,11 @@
return {};
}
-// static
-Tagged<WeakFixedArray> TransitionsAccessor::GetPrototypeTransitions(
- Isolate* isolate, Tagged<Map> map) {
- Tagged<MaybeObject> raw_transitions = map->raw_transitions(kAcquireLoad);
- if (GetEncoding(isolate, raw_transitions) != kFullTransitionArray) {
- return ReadOnlyRoots(isolate).empty_weak_fixed_array();
+Tagged<WeakFixedArray> TransitionsAccessor::GetPrototypeTransitions() {
+ if (!HasPrototypeTransitions()) {
+ return ReadOnlyRoots(isolate_).empty_weak_fixed_array();
}
- Tagged<TransitionArray> transition_array =
- GetTransitionArray(isolate, raw_transitions);
- if (!transition_array->HasPrototypeTransitions()) {
- return ReadOnlyRoots(isolate).empty_weak_fixed_array();
- }
- return transition_array->GetPrototypeTransitions();
+ return transitions()->GetPrototypeTransitions();
}
// static
diff --git a/src/objects/transitions.h b/src/objects/transitions.h
index 408bcaf..d7e948e 100644
--- a/src/objects/transitions.h
+++ b/src/objects/transitions.h
@@ -185,8 +185,13 @@
static bool PutPrototypeTransition(Isolate* isolate, DirectHandle<Map>,
DirectHandle<Object> prototype,
DirectHandle<Map> target_map);
- static std::optional<Tagged<Map>> GetPrototypeTransition(
+
+ std::optional<Tagged<Map>> GetPrototypeTransition(Tagged<Object> prototype);
+ // Convenient helper for calling from the main thread, for other cases
+ // use the instance method above.
+ static inline std::optional<Tagged<Map>> GetPrototypeTransition(
Isolate* isolate, Tagged<Map> map, Tagged<Object> prototype);
+
bool HasPrototypeTransitions();
// During the first-time Map::Update and Map::TryUpdate, the migration target
@@ -267,8 +272,7 @@
static void SetPrototypeTransitions(
Isolate* isolate, DirectHandle<Map> map,
DirectHandle<WeakFixedArray> proto_transitions);
- static Tagged<WeakFixedArray> GetPrototypeTransitions(Isolate* isolate,
- Tagged<Map> map);
+ Tagged<WeakFixedArray> GetPrototypeTransitions();
static void InsertHelper(Isolate* isolate, DirectHandle<Map> map,
DirectHandle<Name> name, DirectHandle<Map> target,
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