High watch CVSS 8.8 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in V8
DescriptionInappropriate implementation in V8
ComponentV8
Sub-componentInspector
Bug ClassLogic Error
Action tierwatch — dos-class only — no demonstrated corruption path
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker516311623
Fix commit772a0f365b17 (v8/v8) +111/-2
CWECWE-122 (Heap buffer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-25 (per vendor advisory)
Fix landed2026-05-26 (public git)
Disclosed2026-06-02
Patch gap7 days — fix public in source before the advisory

Background

Command Line API
the set of debugging helper globals (e.g. $0, $_, inspect) that V8’s inspector injects into the evaluation scope when a CDP Runtime.evaluate/Debugger.evaluateOnCallFrame call passes includeCommandLineAPI: true.
`InjectedScript::CallFrameScope` / `scope.installCommandLineAPI()`
the RAII scope used while evaluating on a paused frame; installCommandLineAPI() writes the helper properties onto the global/context object.
`scope.tryCatch()`
the v8::TryCatch embedded in the scope that captures any JavaScript exception raised by V8 operations performed inside the scope.
Frozen global + strict frame
after Object.freeze(globalThis), writing a property to the global object throws TypeError under strict-mode semantics rather than silently failing as it does in sloppy mode.

Root Cause Analysis

In V8DebuggerAgentImpl::evaluateOnCallFrame (in v8-debugger-agent-impl.cc) and V8RuntimeAgentImpl::evaluate (in v8-runtime-agent-impl.cc), the pre-patch code called scope.installCommandLineAPI() unconditionally when includeCommandLineAPI was set and never inspected the result. When the global object is frozen and a strict-mode frame is on the stack, installing the helper properties triggers a TypeError inside V8, leaving a caught/pending exception in the scope’s tryCatch() that the code then failed to observe. Execution proceeded past the failed installation into the rest of the evaluation path with an inconsistent inspector state, which is the “inappropriate implementation” the advisory describes (the regression test title asserts it “does not crash”).

The fix adds if (scope.tryCatch().HasCaught()) immediately after installCommandLineAPI() and returns a CDP Response::ServerError("Failed to install command line API") (or callback->sendFailure(...) in the runtime agent) before any further evaluation runs. Because the check executes before the subsequent ignoreExceptionsAndMuteConsole()/evaluation steps, the failing call is now aborted cleanly instead of continuing in a corrupt state. The regression test confirms the strict frame now yields the -32000 Failed to install command line API error while the sloppy frame (where the write silently no-ops) still returns 4.

Key insight
The single mistake was invoking installCommandLineAPI() and ignoring that it can raise a JavaScript exception; the fix inspects scope.tryCatch().HasCaught() right after the call and gracefully fails the CDP request instead of proceeding with an unhandled pending exception.

Attack Path

  1. Attach a debugger session A client with CDP access (DevTools, or an attacker able to drive the inspector protocol) enables Debugger/Runtime on a target realm.
  2. Freeze the global under strict mode Script runs "use strict"; Object.freeze(globalThis) and hits a debugger breakpoint so a strict-mode frame is paused on the stack.
  3. Evaluate with the Command Line API The client issues Runtime.evaluate/Debugger.evaluateOnCallFrame with includeCommandLineAPI: true, causing installCommandLineAPI() to attempt property writes on the frozen global.
  4. Trigger the unchecked exception The strict-mode write raises TypeError; pre-patch, the caught exception is not observed and evaluation continues in an inconsistent state (inferred: leads to the crash/misbehavior noted by the regression test), rather than returning a clean CDP error.

Impact Assessment

An attacker (or misbehaving CDP client) can drive the inspector into an inappropriate/unstable state — the advisory classifies this as a LogicError / “Inappropriate implementation in V8,” with the regression test framed around avoiding a crash. Preconditions are an active inspector session and the ability to pause on a strict-mode frame with a frozen global object; the effect is confined to the V8 inspector path and requires debugging to be enabled. No memory-corruption or code-execution primitive is demonstrated by the patch (any escalation beyond the logic/stability fault is not established).

Files Changed

  • src/inspector/v8-debugger-agent-impl.cc
  • src/inspector/v8-runtime-agent-impl.cc
  • test/inspector/regress/regress-crbug-516311623-expected.txt
  • test/inspector/regress/regress-crbug-516311623.js

Audit Directions

  • Unchecked inspector JS operations
    Search inspector code for calls that execute JavaScript or mutate context objects (property installs, formatter setup) that do not consult tryCatch().HasCaught() before continuing.
  • Strict vs sloppy divergence
    Review paths whose behavior silently no-ops in sloppy mode but throws in strict mode (writes to frozen/non-writable objects), and confirm both outcomes are handled.
  • RAII scope error propagation
    Audit CallFrameScope/ObjectScope/ContextScope consumers to ensure every operation that can leave a pending exception is checked before the scope’s remaining steps run.
From 772a0f365b170021dd9ab2c6549a82c4a18501aa Mon Sep 17 00:00:00 2001
From: Simon Zünd <szuend@chromium.org>
Date: Tue, 26 May 2026 11:08:46 +0000
Subject: [PATCH] [inspector] Report a CDP error when installCommandLineAPI fails

Trying to install the command line API function fails on a frozen
globalThis with strict mode frames on the stack. We should gracefully
fail the CDP call in this case.

R=kimanh@chromium.org

Fixed: 516311623
Change-Id: Ib7b731f2d208ca2ccf932528fb3a9cf9b4ad9af5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7876001
Reviewed-by: Kim-Anh Tran <kimanh@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/main@{#107558}
---

diff --git a/src/inspector/v8-debugger-agent-impl.cc b/src/inspector/v8-debugger-agent-impl.cc
index cc5b275..b95f582 100644
--- a/src/inspector/v8-debugger-agent-impl.cc
+++ b/src/inspector/v8-debugger-agent-impl.cc
@@ -1725,7 +1725,13 @@
   InjectedScript::CallFrameScope scope(m_session, callFrameId);
   Response response = scope.initialize();
   if (!response.IsSuccess()) return response;
-  if (includeCommandLineAPI.value_or(false)) scope.installCommandLineAPI();
+
+  if (includeCommandLineAPI.value_or(false)) {
+    scope.installCommandLineAPI();
+    if (scope.tryCatch().HasCaught()) {
+      return Response::ServerError("Failed to install command line API");
+    }
+  }
   if (silent.value_or(false)) scope.ignoreExceptionsAndMuteConsole();
 
   int frameOrdinal = static_cast<int>(scope.frameOrdinal());
diff --git a/src/inspector/v8-runtime-agent-impl.cc b/src/inspector/v8-runtime-agent-impl.cc
index b8d6d44..e6ce30c 100644
--- a/src/inspector/v8-runtime-agent-impl.cc
+++ b/src/inspector/v8-runtime-agent-impl.cc
@@ -386,7 +386,14 @@
   if (silent.value_or(false)) scope.ignoreExceptionsAndMuteConsole();
   if (userGesture.value_or(false)) scope.pretendUserGesture();
 
-  if (includeCommandLineAPI.value_or(false)) scope.installCommandLineAPI();
+  if (includeCommandLineAPI.value_or(false)) {
+    scope.installCommandLineAPI();
+    if (scope.tryCatch().HasCaught()) {
+      callback->sendFailure(
+          Response::ServerError("Failed to install command line API"));
+      return;
+    }
+  }
 
   const bool replMode = maybeReplMode.value_or(false);
 
diff --git a/test/inspector/regress/regress-crbug-516311623-expected.txt b/test/inspector/regress/regress-crbug-516311623-expected.txt
new file mode 100644
index 0000000..717d571
--- /dev/null
+++ b/test/inspector/regress/regress-crbug-516311623-expected.txt
@@ -0,0 +1,24 @@
+Test that Runtime.evaluate with includeCommandLineAPI does not crash when global object is frozen.
+Running test: testStrictFrame
+Paused in strict mode.
+Evaluating with includeCommandLineAPI while paused in strict...
+{
+    error : {
+        code : -32000
+        message : Failed to install command line API
+    }
+    id : <messageId>
+}
+Running test: testSloppyFrame
+Paused in sloppy mode.
+Evaluating with includeCommandLineAPI while paused in sloppy...
+{
+    id : <messageId>
+    result : {
+        result : {
+            description : 4
+            type : number
+            value : 4
+        }
+    }
+}
diff --git a/test/inspector/regress/regress-crbug-516311623.js b/test/inspector/regress/regress-crbug-516311623.js
new file mode 100644
index 0000000..b083cd4
--- /dev/null
+++ b/test/inspector/regress/regress-crbug-516311623.js
@@ -0,0 +1,72 @@
+// 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.
+
+const {session, Protocol} = InspectorTest.start(
+    'Test that Runtime.evaluate with includeCommandLineAPI does not crash when global object is frozen.');
+
+InspectorTest.runAsyncTestSuite([
+  async function testStrictFrame() {
+    await Protocol.Debugger.enable();
+
+    // Define both strict and sloppy mode functions upfront before freezing the global object.
+    await Protocol.Runtime.evaluate({
+      expression: `
+        const global = this;
+        function runStrict() {
+          "use strict";
+          Object.freeze(global);
+          debugger;
+        }
+        function runSloppy() {
+          debugger;
+        }
+      `
+    });
+
+    // Run the strict function. It should pause.
+    const pausedPromise = Protocol.Debugger.oncePaused();
+    Protocol.Runtime.evaluate({ expression: 'runStrict()' });
+    await pausedPromise;
+    InspectorTest.log('Paused in strict mode.');
+
+    // Evaluate with includeCommandLineAPI: true.
+    // In strict mode, the command line API installation throws TypeError.
+    // The evaluation aborts early and returns the TypeError object.
+    InspectorTest.log('Evaluating with includeCommandLineAPI while paused in strict...');
+    const evalResponse = await Protocol.Runtime.evaluate({
+      expression: '2 + 2',
+      includeCommandLineAPI: true
+    });
+
+    // Log the full response to see the error.
+    InspectorTest.logMessage(evalResponse);
+
+    await Protocol.Debugger.resume();
+  },
+
+  async function testSloppyFrame() {
+    // Note: Global is already frozen from the previous test.
+    // runSloppy is also already defined.
+
+    // Run the sloppy function. It should pause.
+    const pausedPromise = Protocol.Debugger.oncePaused();
+    Protocol.Runtime.evaluate({ expression: 'runSloppy()' });
+    await pausedPromise;
+    InspectorTest.log('Paused in sloppy mode.');
+
+    // Evaluate with includeCommandLineAPI: true.
+    // In sloppy mode, the command line API installation fails silently.
+    // The evaluation proceeds and returns 4.
+    InspectorTest.log('Evaluating with includeCommandLineAPI while paused in sloppy...');
+    const evalResponse = await Protocol.Runtime.evaluate({
+      expression: '2 + 2',
+      includeCommandLineAPI: true
+    });
+
+    // Log the full response, should be value: 4.
+    InspectorTest.logMessage(evalResponse);
+
+    await Protocol.Debugger.resume();
+  }
+]);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/test/inspector/regress/regress-crbug-516311623-expected.txt b/test/inspector/regress/regress-crbug-516311623-expected.txt
new file mode 100644
index 0000000..717d571
--- /dev/null
+++ b/test/inspector/regress/regress-crbug-516311623-expected.txt
@@ -0,0 +1,24 @@
+Test that Runtime.evaluate with includeCommandLineAPI does not crash when global object is frozen.
+Running test: testStrictFrame
+Paused in strict mode.
+Evaluating with includeCommandLineAPI while paused in strict...
+{
+    error : {
+        code : -32000
+        message : Failed to install command line API
+    }
+    id : <messageId>
+}
+Running test: testSloppyFrame
+Paused in sloppy mode.
+Evaluating with includeCommandLineAPI while paused in sloppy...
+{
+    id : <messageId>
+    result : {
+        result : {
+            description : 4
+            type : number
+            value : 4
+        }
+    }
+}
diff --git a/test/inspector/regress/regress-crbug-516311623.js b/test/inspector/regress/regress-crbug-516311623.js
new file mode 100644
index 0000000..b083cd4
--- /dev/null
+++ b/test/inspector/regress/regress-crbug-516311623.js
@@ -0,0 +1,72 @@
+// 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.
+
+const {session, Protocol} = InspectorTest.start(
+    'Test that Runtime.evaluate with includeCommandLineAPI does not crash when global object is frozen.');
+
+InspectorTest.runAsyncTestSuite([
+  async function testStrictFrame() {
+    await Protocol.Debugger.enable();
+
+    // Define both strict and sloppy mode functions upfront before freezing the global object.
+    await Protocol.Runtime.evaluate({
+      expression: `
+        const global = this;
+        function runStrict() {
+          "use strict";
+          Object.freeze(global);
+          debugger;
+        }
+        function runSloppy() {
+          debugger;
+        }
+      `
+    });
+
+    // Run the strict function. It should pause.
+    const pausedPromise = Protocol.Debugger.oncePaused();
+    Protocol.Runtime.evaluate({ expression: 'runStrict()' });
+    await pausedPromise;
+    InspectorTest.log('Paused in strict mode.');
+
+    // Evaluate with includeCommandLineAPI: true.
+    // In strict mode, the command line API installation throws TypeError.
+    // The evaluation aborts early and returns the TypeError object.
+    InspectorTest.log('Evaluating with includeCommandLineAPI while paused in strict...');
+    const evalResponse = await Protocol.Runtime.evaluate({
+      expression: '2 + 2',
+      includeCommandLineAPI: true
+    });
+
+    // Log the full response to see the error.
+    InspectorTest.logMessage(evalResponse);
+
+    await Protocol.Debugger.resume();
+  },
+
+  async function testSloppyFrame() {
+    // Note: Global is already frozen from the previous test.
+    // runSloppy is also already defined.
+
+    // Run the sloppy function. It should pause.
+    const pausedPromise = Protocol.Debugger.oncePaused();
+    Protocol.Runtime.evaluate({ expression: 'runSloppy()' });
+    await pausedPromise;
+    InspectorTest.log('Paused in sloppy mode.');
+
+    // Evaluate with includeCommandLineAPI: true.
+    // In sloppy mode, the command line API installation fails silently.
+    // The evaluation proceeds and returns 4.
+    InspectorTest.log('Evaluating with includeCommandLineAPI while paused in sloppy...');
+    const evalResponse = await Protocol.Runtime.evaluate({
+      expression: '2 + 2',
+      includeCommandLineAPI: true
+    });
+
+    // Log the full response, should be value: 4.
+    InspectorTest.logMessage(evalResponse);
+
+    await Protocol.Debugger.resume();
+  }
+]);
Loading diff…

Original Bug Report

reported by 24...@project.gserviceaccount.com

DCHECK failure in !isolate->has_exception() in execution.cc

Detailed Report: https://clusterfuzz.com/testcase?key=4548820230766592

Fuzzer: ochang_js_fuzzer Job Type: linux_asan_d8_dbg Platform Id: linux

Crash Type: DCHECK failure Crash Address: Crash State: !isolate->has_exception() in execution.cc v8::internal::Invoke v8::internal::Execution::CallScript

Sanitizer: address (ASAN)

Regressed: https://clusterfuzz.com/revisions?job=linux_asan_d8_dbg&range=91856:91857

Reproducer Testcase: https://clusterfuzz.com/download?testcase_id=4548820230766592

Issue filed automatically.

To reproduce this, please build the target in this report and run it against the reproducer testcase. Please use the GN arguments provided at bottom of this report when building the binary.

If you have trouble reproducing, please also export the environment variables listed under “[Environment]” in the crash stacktrace.

If you have any feedback on reproducing test cases, let us know at https://forms.gle/Yh3qCYFveHj6E5jz5 so we can improve.

View on issue tracker