Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient control flow management in DevTools
DescriptionInsufficient control flow management in DevTools
ComponentDevTools
Bug ClassLogic Error
Tracker532914769
Fix commitede68f0c6b64 (v8/v8) +61/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
ownKeys
test/inspector/runtime/map-set-entry-preview-sideeffects.js
modified
getOwnPropertyDescriptor
test/inspector/runtime/map-set-entry-preview-sideeffects.js
modified

Files Changed

  • src/debug/debug-property-iterator.cc
  • test/inspector/runtime/map-set-entry-preview-sideeffects-expected.txt
  • test/inspector/runtime/map-set-entry-preview-sideeffects.js
From ede68f0c6b6400c94e84f98d38e77a5226fcd181 Mon Sep 17 00:00:00 2001
From: Yang Guo <[email protected]>
Date: Thu, 09 Jul 2026 17:03:40 +0000
Subject: [PATCH] [inspector] Prevent synchronous JS execution during Map/Set entry preview

During V8 Inspector preview generation (buildObjectPreviewInternal),
when generating nested previews for Map/Set entry keys and values
(forEntry = true), getPropertiesForPreview previously called
ValueMirror::getProperties with ownProperties = false.

Furthermore, when DebugPropertyIterator advanced along the prototype
chain of an object during inspection or when initializing on an object
with zero own properties (DebugPropertyIterator::Create), it would land
on any JSProxy present in the prototype chain via
PrototypeIterator::AdvanceIgnoringProxies(). Once on the proxy,
KeyAccumulator::GetKeys() and property descriptor lookups would
invoke page-controlled JS traps (ownKeys and getOwnPropertyDescriptor)
synchronously inside preview generation.

This change fixes the issue by:
1. Updating DebugPropertyIterator::AdvanceToPrototype() to loop over and
skip any JSProxy objects encountered when traversing prototype chains,
ensuring proxy traps are never invoked during debug property iteration.
2. Updating getPropertiesForPreview(..., bool forEntry, ...) and
passing forEntry as ownProperties to ValueMirror::getProperties().
When generating previews for Map/Set entries (forEntry = true),
property retrieval is restricted to own properties only (ownProperties = true),
avoiding redundant prototype traversals.

TAG=agy
CONV=eb961c5d-e12d-4826-ab96-b720eec9a825

Fixed: 532914769
Change-Id: I5be13911a5a0aec8d158cc709ddcd3e639fc72b0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8067733
Reviewed-by: Simon Zünd <[email protected]>
Auto-Submit: Yang Guo <[email protected]>
Commit-Queue: Simon Zünd <[email protected]>
Cr-Commit-Position: refs/heads/main@{#108569}
---

diff --git a/src/debug/debug-property-iterator.cc b/src/debug/debug-property-iterator.cc
index 00dd8d3..9d49620 100644
--- a/src/debug/debug-property-iterator.cc
+++ b/src/debug/debug-property-iterator.cc
@@ -50,6 +50,11 @@
   is_own_ = false;
   if (!prototype_iterator_.HasAccess()) is_done_ = true;
   prototype_iterator_.AdvanceIgnoringProxies();
+  while (!prototype_iterator_.IsAtEnd() &&
+         IsJSProxy(
+             *PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_))) {
+    prototype_iterator_.AdvanceIgnoringProxies();
+  }
   if (prototype_iterator_.IsAtEnd()) is_done_ = true;
 }
 
diff --git a/test/inspector/runtime/map-set-entry-preview-sideeffects-expected.txt b/test/inspector/runtime/map-set-entry-preview-sideeffects-expected.txt
new file mode 100644
index 0000000..b26aa73
--- /dev/null
+++ b/test/inspector/runtime/map-set-entry-preview-sideeffects-expected.txt
@@ -0,0 +1,7 @@
+Checks that Map/Set entry preview does not trigger Proxy traps on prototype chain.
+
+Running test: testMapSetEntryPreviewSideEffects
+{
+    gOPDCalled : 0
+    ownKeysCalled : 0
+}
diff --git a/test/inspector/runtime/map-set-entry-preview-sideeffects.js b/test/inspector/runtime/map-set-entry-preview-sideeffects.js
new file mode 100644
index 0000000..c6bdccb
--- /dev/null
+++ b/test/inspector/runtime/map-set-entry-preview-sideeffects.js
@@ -0,0 +1,49 @@
+// 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.
+
+let {session, contextGroup, Protocol} = InspectorTest.start(
+    'Checks that Map/Set entry preview does not trigger Proxy traps on prototype chain.');
+
+contextGroup.addScript(`
+function testMapSetEntryPreview() {
+  let ownKeysCalled = 0;
+  let gOPDCalled = 0;
+  const target = { foo: 1 };
+  const evilProto = new Proxy(target, {
+    ownKeys(t) {
+      ownKeysCalled++;
+      return Reflect.ownKeys(t);
+    },
+    getOwnPropertyDescriptor(t, prop) {
+      gOPDCalled++;
+      return Reflect.getOwnPropertyDescriptor(t, prop);
+    }
+  });
+  const entryVal = {};
+  Object.setPrototypeOf(entryVal, evilProto);
+  const map = new Map([['key', entryVal]]);
+  const set = new Set([entryVal]);
+  return { map, set, getTrapsCalled: () => ({ ownKeysCalled, gOPDCalled }) };
+}
+`);
+
+InspectorTest.runAsyncTestSuite([
+  async function testMapSetEntryPreviewSideEffects() {
+    await Protocol.Runtime.enable();
+    await Protocol.Runtime.evaluate({
+      expression: 'var { map, set, getTrapsCalled } = testMapSetEntryPreview(); map',
+      generatePreview: true
+    });
+    await Protocol.Runtime.evaluate({
+      expression: 'set',
+      generatePreview: true
+    });
+    const result = await Protocol.Runtime.evaluate({
+      expression: 'getTrapsCalled()',
+      returnByValue: true
+    });
+    InspectorTest.logObject(result.result.result.value);
+    await Protocol.Runtime.disable();
+  }
+]);
Loading diff…

Original Bug Report

reported by [email protected]

Synchronous JS execution during Map/Set entry preview in V8 Inspector

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: The V8 inspector’s nested preview generation for Map and Set entries can traverse the prototype chain of entry values with ownProperties=false. If an entry value inherits from a Proxy prototype, this prototype walk can synchronously trigger page-controlled proxy traps inside the inspector context. This potential re-entrancy primitive could allow an attacker to run page JavaScript during preview generation and potentially cause memory corruption.

Affected files:

  • v8/src/inspector/value-mirror.cc
  • v8/src/debug/debug-property-iterator.cc

Estimated timestamp from git blame: 2018-10-31

Description

During V8 Inspector preview generation, ObjectMirror::buildObjectPreviewInternal handles Map/Set/WeakMap/WeakSet entries by looping through them and initiating nested preview generation for each entry’s key and value:

// v8/src/inspector/value-mirror.cc
for (const auto& entry : entries) {
  std::unique_ptr<ObjectPreview> valuePreview;
  entry.value->buildEntryPreview(context, nameLimit, indexLimit, &valuePreview);

buildEntryPreview re-enters buildObjectPreviewInternal with forEntry=true:

void buildEntryPreview(...) const override {
  buildObjectPreviewInternal(context, true /* forEntry */,
                             false /* generatePreviewForTable */, nameLimit,
                             indexLimit, result);
}

During this nested call, the inspector retrieves the properties of the entry value for the preview, invoking getPropertiesForPreview which delegates to ValueMirror::getProperties with ownProperties set to false:

return ValueMirror::getProperties(context, object, false, false, false, &accumulator);

Because ownProperties is false, the property iterator (v8::debug::PropertyIterator) is permitted to traverse beyond the object’s own properties onto its prototype chain. If the prototype of the entry value is a JSProxy, the iterator lands on the proxy. When fetching keys (KeyAccumulator::GetKeys) or querying property attributes/descriptors, the inspector synchronously invokes the proxy’s ownKeys and getOwnPropertyDescriptor traps, leading to page-controlled JS execution.

Potential Call Flow and Execution Path

Based on static code analysis, the following sequence represents the potential execution path:

  1. The page registers a custom Proxy evilProto with traps for ownKeys and getOwnPropertyDescriptor and sets it as the prototype of an object entryVal (Object.setPrototypeOf(entryVal, evilProto)).
  2. The page logs a Map containing entryVal to the console (e.g., console.log(new Map([['key', entryVal]]))).
  3. In response, V8 inspector routes the log to V8ConsoleMessageStorage::addMessage and eventually to V8ConsoleMessage::reportToFrontend with generatePreview=true.
  4. V8InspectorSessionImpl::wrapObject invokes InjectedScript::wrapObject with WrapMode::kPreview.
  5. ObjectMirror::buildRemoteObject calls buildObjectPreview to recursively generate the preview for the Map.
  6. buildObjectPreviewInternal loops through the Map’s entry values and calls entry.value->buildEntryPreview.
  7. A nested call to buildObjectPreviewInternal on entryVal triggers getPropertiesForPreview with ownProperties=false.
  8. v8::debug::PropertyIterator traverses onto evilProto.
  9. KeyAccumulator::GetKeys and JSReceiver::GetOwnPropertyDescriptor dispatch to the proxy, running the page’s custom ownKeys and getOwnPropertyDescriptor traps synchronously.

Note: These are potential execution steps derived from static code analysis; our tooling does not currently have the capability to run code or compile a live proof-of-concept.

Potential Security Impact

If validated, synchronous page JavaScript execution in the middle of inspector preview generation constitutes a re-entrancy vulnerability. An attacker could potentially leverage this synchronous execution window to mutate V8/Inspector internal state, detach frames/contexts, or free underlying structures while they are still being traversed on the call stack, potentially leading to Use-After-Free (UAF) or other memory safety issues inside the sandboxed renderer process.

Suggested Remediation

When generating nested previews for Map/Set entry keys and values (where forEntry is true), the inspector should avoid traversing onto JSProxy prototypes, or restrict property retrieval to own properties only (i.e., setting ownProperties to true). Specifically, in getPropertiesForPreview or buildObjectPreviewInternal, verify whether the target is being evaluated as an entry value, and avoid non-own property collection on objects with prototype chains containing proxies.

Evaluated with Chrome root at commit: 84065d9121f6e48f67755f0ae963cc09617e5c85


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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.

View on issue tracker