Chrome · DevTools
CVE-2026-79148
Logic Error in DevTools
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/inspector/v8-debugger-agent-impl.cc |
modified | |
iftest/inspector/debugger/set-variable-value-off-by-one.js |
modified |
Files Changed
src/inspector/v8-debugger-agent-impl.cctest/inspector/debugger/set-variable-value-off-by-one-expected.txttest/inspector/debugger/set-variable-value-off-by-one.js
Patch
From 741c0f4ad7bd87d50294e67e4f01bb9b382d853b Mon Sep 17 00:00:00 2001
From: Etienne Bergeron <etienneb@google.com>
Date: Wed, 15 Jul 2026 17:47:35 +0000
Subject: [PATCH] [inspector] Fix off-by-one boundary check in setVariableValue
When V8DebuggerAgentImpl::setVariableValue iterates through scopes,
requesting a scopeNumber equal to the scope chain length (N) causes the
iterator to advance past the last scope so scopeIterator->Done()
becomes true, while scopeNumber simultaneously decrements to 0.
Because the boundary check only verified (scopeNumber != 0), it failed
to detect iterator exhaustion when scopeNumber reached 0 on the same
step as Done(). Subsequent calls to scopeIterator->SetVariableValue()
dereferenced an invalid iterator, triggering a DCHECK failure in debug
builds and a null pointer dereference in release builds.
This CL updates the boundary guard to check (scopeNumber != 0 ||
scopeIterator->Done()), rejecting out-of-bounds scope numbers with a
protocol error response.
Bug: 532303080
Change-Id: I86ab290c66deff16259b82832d29db5531d60599
Fixed: 532303080
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8100344
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Cr-Commit-Position: refs/heads/main@{#108706}
---
diff --git a/src/inspector/v8-debugger-agent-impl.cc b/src/inspector/v8-debugger-agent-impl.cc
index abd10db..466b784 100644
--- a/src/inspector/v8-debugger-agent-impl.cc
+++ b/src/inspector/v8-debugger-agent-impl.cc
@@ -1836,7 +1836,7 @@
--scopeNumber;
scopeIterator->Advance();
}
- if (scopeNumber != 0) {
+ if (scopeNumber != 0 || scopeIterator->Done()) {
return Response::ServerError("Could not find scope with given number");
}
diff --git a/test/inspector/debugger/set-variable-value-off-by-one-expected.txt b/test/inspector/debugger/set-variable-value-off-by-one-expected.txt
new file mode 100644
index 0000000..0ee3ae6
--- /dev/null
+++ b/test/inspector/debugger/set-variable-value-off-by-one-expected.txt
@@ -0,0 +1,5 @@
+Tests setVariableValue with scopeNumber equal to scopeChain length (off-by-one boundary)
+Paused in test(). Scope chain length: 2
+Calling setVariableValue with scopeNumber = 2
+Received error (expected): Could not find scope with given number
+test() finished executing.
diff --git a/test/inspector/debugger/set-variable-value-off-by-one.js b/test/inspector/debugger/set-variable-value-off-by-one.js
new file mode 100644
index 0000000..262e17a
--- /dev/null
+++ b/test/inspector/debugger/set-variable-value-off-by-one.js
@@ -0,0 +1,46 @@
+// 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 { contextGroup, Protocol } = InspectorTest.start(
+ 'Tests setVariableValue with scopeNumber equal to scopeChain length (off-by-one boundary)'
+);
+
+contextGroup.addScript(`
+function test() {
+ let a = 10;
+ debugger;
+ return a;
+}
+`);
+
+Protocol.Debugger.enable();
+Protocol.Debugger.onPaused(async message => {
+ const callFrame = message.params.callFrames[0];
+ const callFrameId = callFrame.callFrameId;
+ const numScopes = callFrame.scopeChain.length;
+
+ InspectorTest.log('Paused in test(). Scope chain length: ' + numScopes);
+
+ // Call setVariableValue with scopeNumber = numScopes (off-by-one out-of-bounds)
+ InspectorTest.log('Calling setVariableValue with scopeNumber = ' + numScopes);
+ const response = await Protocol.Debugger.setVariableValue({
+ scopeNumber: numScopes,
+ variableName: 'a',
+ newValue: { value: 99 },
+ callFrameId
+ });
+
+ if (response.error) {
+ InspectorTest.log('Received error (expected): ' + response.error.message);
+ } else {
+ InspectorTest.log('SUCCESS (UNEXPECTED! Should have returned error)');
+ }
+
+ await Protocol.Debugger.resume();
+});
+
+Protocol.Runtime.evaluate({ expression: 'test()' }).then(response => {
+ InspectorTest.log('test() finished executing.');
+ InspectorTest.completeTest();
+});
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