Low watch CVSS 8.8 chrome Type Confusion 🔧 Commit mapped

Overview

Low
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType confusion in V8
DescriptionType confusion in V8
ComponentV8
Sub-componentWASM
Bug ClassType Confusion
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
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.
Tracker531245718
Fix commit01b5e5a2cda3 (v8/v8) +14/-2
CWECWE-843 (Type confusion)
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-07-04 (per vendor advisory)
Fix landed2026-07-08 (public git)
Disclosed2026-08-25
Patch gap48 days — fix public in source before the advisory

Background

`WasmFrame`
V8 stack-frame abstraction for an executing WebAssembly function, exposing accessors such as pc(), fp(), and callee_fp().
`WASM_DEBUG_BREAK` frame
a synthetic stack frame pushed when execution stops at a Wasm breakpoint, whose frame pointer holds the spilled register values the debugger reads.
`callee_fp()`
the frame pointer of the frame called by the current WasmFrame, which is only meaningful as a debug-break register source when the callee actually is a WASM_DEBUG_BREAK frame.
Frame type marker
an intptr_t stored at TypedFrameConstants::kFrameTypeOffset inside a typed frame, comparable against StackFrame::TypeToMarker(...) to identify the frame’s kind.

Root Cause Analysis

When the debugger inspected local and stack values for a paused Wasm frame, it passed frame->callee_fp() directly to debug_info->GetLocalValue and debug_info->GetStackValue as the debug_break_fp used to locate spilled register values. The code assumed the callee frame was always a WASM_DEBUG_BREAK frame, but when a trap occurs immediately after a breakpoint the callee frame can be an unrelated C++ frame with a different layout, violating the invariant that debug_break_fp points at a debug-break register save area. Register-backed WasmValues were then reconstructed by reading register offsets relative to that mismatched frame pointer, producing a type-confused read of arbitrary stack memory as a typed Wasm value.

The fix introduces GetDebugBreakFp, which reads the callee frame’s type marker and returns kNullAddress unless it equals the WASM_DEBUG_BREAK marker, so a non-debug-break callee no longer supplies a frame pointer. GetLocalValue/GetStackValue now additionally bail out and return an empty value when debug_break_fp == kNullAddress before dereferencing a register, closing the confusion.

Key insight
The single mistake was trusting callee_fp() to always denote a WASM_DEBUG_BREAK frame without validating its frame-type marker; the fix verifies that marker (GetDebugBreakFp) and refuses register retrieval when it is absent.

Attack Path

  1. Set a Wasm breakpoint An attacker-controlled page installs a breakpoint in a WebAssembly module and drives execution to hit it, entering the debugger’s value-inspection path.
  2. Trigger a trap after the break Craft the module so a trap fires immediately after the breakpoint, leaving a callee C++ frame that is not a WASM_DEBUG_BREAK frame beneath the paused WasmFrame.
  3. Force value inspection Cause the debugger to enumerate locals or stack slots, invoking debug_info->GetLocalValue/GetStackValue with the mismatched frame->callee_fp() as debug_break_fp.
  4. Misread register slots For a register-backed value, GetLocalValue computes an address off the wrong frame pointer and reinterprets unrelated stack bytes as a typed Wasm value, achieving type confusion / out-of-bounds stack disclosure.

Impact Assessment

An attacker who can control a page with DevTools/debugger interaction gains a type-confused read of stack memory reinterpreted as Wasm values, leaking pointers or other sensitive data from the renderer’s C++ stack. This occurs in the V8 renderer process and can seed further exploitation of the sandbox. The precondition is exercising the Wasm debugger so a breakpoint is followed by a trap with a non-debug-break callee frame.

Changed Functions

FunctionChangeNotes
for
src/debug/debug-wasm-objects.cc
modified

Files Changed

  • src/debug/debug-wasm-objects.cc
  • src/wasm/wasm-debug.cc

Audit Directions

  • Unvalidated frame pointers
    Search for other debugger or stack-walker paths that consume callee_fp(), caller_fp(), or raw fp values without first checking a StackFrame type marker.
  • Frame-type assumptions
    Review code that assumes a specific adjacent frame layout (e.g., WASM_DEBUG_BREAK, builtin exit, or trap frames) and reads spilled registers or arguments by fixed offset without confirming the frame kind.
  • Register reconstruction
    Audit WasmValue/LiftoffRegister reconstruction sites that translate register codes to memory addresses relative to a caller-supplied base, ensuring each base is verified non-null and correctly typed before dereference.
From 01b5e5a2cda3d4d89434aa614753798b17bad438 Mon Sep 17 00:00:00 2001
From: Clemens Backes <clemensb@chromium.org>
Date: Wed, 08 Jul 2026 10:21:45 +0200
Subject: [PATCH] [wasm][debug] Fix trap frame confusion in Wasm debugger

When a trap occurs immediately after a breakpoint, the debugger might
attempt to inspect values from an unrelated C++ stack frame if it
misidentifies the callee frame layout.

This CL adds a verification step to ensure the callee frame is a valid
WASM_DEBUG_BREAK frame before using its frame pointer for register
retrieval.

TAG=agy
R=szuend@chromium.org

Bug: 531245718
Change-Id: I2654d29e10d32b09891db443e016f1d606b8b572
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8053369
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/main@{#108515}
---
diff --git a/src/debug/debug-wasm-objects.cc b/src/debug/debug-wasm-objects.cc
index 9ed9085..bbf988d 100644
--- a/src/debug/debug-wasm-objects.cc
+++ b/src/debug/debug-wasm-objects.cc
@@ -35,6 +35,17 @@
       base::VectorOf(sb.start(), sb.length()));
 }
 
+Address GetDebugBreakFp(WasmFrame* frame) {
+  Address callee_fp = frame->callee_fp();
+  if (callee_fp == kNullAddress) return kNullAddress;
+  intptr_t marker =
+      base::Memory<intptr_t>(callee_fp + TypedFrameConstants::kFrameTypeOffset);
+  if (marker != StackFrame::TypeToMarker(StackFrame::WASM_DEBUG_BREAK)) {
+    return kNullAddress;
+  }
+  return callee_fp;
+}
+
 enum DebugProxyId {
   kFunctionsProxy,
   kGlobalsProxy,
@@ -431,7 +442,7 @@
     for (int i = 0; i < count; ++i) {
       auto value = WasmValueObject::New(
           isolate, debug_info->GetLocalValue(i, frame->pc(), frame->fp(),
-                                             frame->callee_fp(), isolate));
+                                             GetDebugBreakFp(frame), isolate));
       values->set(i, *value);
     }
     values->set(count + 0, frame->wasm_instance()->module_object());
@@ -478,7 +489,7 @@
     for (int i = 0; i < count; ++i) {
       auto value = WasmValueObject::New(
           isolate, debug_info->GetStackValue(i, frame->pc(), frame->fp(),
-                                             frame->callee_fp(), isolate));
+                                             GetDebugBreakFp(frame), isolate));
       values->set(i, *value);
     }
     return IndexedDebugProxy::Create(isolate, values);
diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc
index 0a1a6b8..4b2bd13 100644
--- a/src/wasm/wasm-debug.cc
+++ b/src/wasm/wasm-debug.cc
@@ -640,6 +640,7 @@
     }
 
     if (value->is_register()) {
+      if (debug_break_fp == kNullAddress) return {};
       auto reg = LiftoffRegister::from_liftoff_code(value->reg_code);
       auto gp_addr = [debug_break_fp](Register reg) {
         return debug_break_fp +
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.