Low chrome Type Confusion 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType Confusion in V8
DescriptionType Confusion in V8
ComponentV8
Bug ClassType Confusion
Tracker520017306
Fix commit701254bc2334 (v8/v8) +41/-8
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • src/base/macros.h
  • src/codegen/bailout-reason.h
  • src/wasm/baseline/arm/liftoff-assembler-arm-inl.h
  • src/wasm/baseline/arm64/liftoff-assembler-arm64-inl.h
  • src/wasm/baseline/ia32/liftoff-assembler-ia32-inl.h
  • src/wasm/baseline/liftoff-assembler.h
  • src/wasm/baseline/liftoff-compiler.cc
From 701254bc23346416c19c6793da86ba13de35bef4 Mon Sep 17 00:00:00 2001
From: Clemens Backes <[email protected]>
Date: Tue, 16 Jun 2026 14:35:34 +0200
Subject: [PATCH] [wasm][liftoff] Harden OSR state on x64

On x64, Liftoff uses a deferred OSR mechanism where an interrupt sets an
OSR target in a stack slot. This request must be consumed by a
MaybeOSR() call before the next instruction, otherwise type confusion
can occur if execution continues in old code with a mismatched stack.

This CL:
1. Adds missing MaybeOSR() calls after instructions that handle
   interrupts (AtomicWait, MemoryGrow, TableGrow, TableCopy, StructWait,
   RefTest, and various array allocation/initialization builtins).
2. Introduces a debug-only AssertOSREmpty() check at the start of every
   breakable instruction (via EmitDebuggingInfo) to ensure no pending
   OSR request was missed.
3. Refactors OSR-related methods to be strictly architecture-isolated
   to x64, where the deferred mechanism is used.
4. Adds IF_TARGET_ARCH_X64 macro to facilitate this isolation.

[email protected]

TAG=agy

Bug: 520017306
Change-Id: Iba67891bfcd45b1c7cbf9ab03f2e511c007e0d13
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7946779
Reviewed-by: Thibaud Michaud <[email protected]>
Commit-Queue: Clemens Backes <[email protected]>
Cr-Commit-Position: refs/heads/main@{#108048}
---

diff --git a/src/base/macros.h b/src/base/macros.h
index 4355dec8..c2c24e8 100644
--- a/src/base/macros.h
+++ b/src/base/macros.h
@@ -606,6 +606,15 @@
 #define IF_TARGET_ARCH_32_BIT(V, ...) EXPAND(V(__VA_ARGS__))
 #endif  // V8_TARGET_ARCH_64_BIT
 
+// Defines IF_TARGET_ARCH_X64, to be used in macro lists for elements that
+// should only be there if the target architecture is x64.
+#if V8_TARGET_ARCH_X64
+// EXPAND is needed to work around MSVC's broken __VA_ARGS__ expansion.
+#define IF_TARGET_ARCH_X64(V, ...) EXPAND(V(__VA_ARGS__))
+#else
+#define IF_TARGET_ARCH_X64(V, ...)
+#endif  // V8_TARGET_ARCH_X64
+
 // Defines IF_V8_WASM_RANDOM_FUZZERS and IF_NO_V8_WASM_RANDOM_FUZZERS, to be
 // used in macro lists for elements that should only be there/absent when
 // building the Wasm fuzzers.
diff --git a/src/codegen/bailout-reason.h b/src/codegen/bailout-reason.h
index bdae933..be534d6 100644
--- a/src/codegen/bailout-reason.h
+++ b/src/codegen/bailout-reason.h
@@ -129,6 +129,8 @@
   V(k32BitValueInRegisterIsNotSignExtended,                                   \
     "32 bit value in register is not sign-extended")                          \
   V(kUnexpectedSandboxMode, "The sandboxing mode is not as expected")         \
+  IF_TARGET_ARCH_X64(V, kOSREmptyCheckFailed,                                 \
+                     "OSR target slot should be empty")                       \
   V(kLastReason, "")
 
 #define TERMINAL_BAILOUT_MESSAGES_LIST(V)                                  \
diff --git a/src/wasm/baseline/arm/liftoff-assembler-arm-inl.h b/src/wasm/baseline/arm/liftoff-assembler-arm-inl.h
index 1008602..3de6328 100644
--- a/src/wasm/baseline/arm/liftoff-assembler-arm-inl.h
+++ b/src/wasm/baseline/arm/liftoff-assembler-arm-inl.h
@@ -5134,7 +5134,6 @@
   add(sp, sp, Operand(size));
 }
 
-void LiftoffAssembler::MaybeOSR() {}
 
 void LiftoffStackSlots::Construct(int param_slots) {
   DCHECK_LT(0, slots_.size());
diff --git a/src/wasm/baseline/arm64/liftoff-assembler-arm64-inl.h b/src/wasm/baseline/arm64/liftoff-assembler-arm64-inl.h
index 1ebd338a..a2c220c 100644
--- a/src/wasm/baseline/arm64/liftoff-assembler-arm64-inl.h
+++ b/src/wasm/baseline/arm64/liftoff-assembler-arm64-inl.h
@@ -4659,7 +4659,6 @@
   Drop(size, 1);
 }
 
-void LiftoffAssembler::MaybeOSR() {}
 
 void LiftoffStackSlots::Construct(int param_slots) {
   DCHECK_LT(0, slots_.size());
diff --git a/src/wasm/baseline/ia32/liftoff-assembler-ia32-inl.h b/src/wasm/baseline/ia32/liftoff-assembler-ia32-inl.h
index c1b7750..de73188 100644
--- a/src/wasm/baseline/ia32/liftoff-assembler-ia32-inl.h
+++ b/src/wasm/baseline/ia32/liftoff-assembler-ia32-inl.h
@@ -5343,7 +5343,6 @@
   add(esp, Immediate(size));
 }
 
-void LiftoffAssembler::MaybeOSR() {}
 
 void LiftoffStackSlots::Construct(int param_slots) {
   DCHECK_LT(0, slots_.size());
diff --git a/src/wasm/baseline/liftoff-assembler.h b/src/wasm/baseline/liftoff-assembler.h
index 966498d..3d1b96c 100644
--- a/src/wasm/baseline/liftoff-assembler.h
+++ b/src/wasm/baseline/liftoff-assembler.h
@@ -1639,8 +1639,11 @@
   inline void AllocateStackSlot(Register addr, uint32_t size);
   inline void DeallocateStackSlot(uint32_t size);
 
+#if V8_TARGET_ARCH_X64
   // Instrumentation for shadow-stack-compatible OSR on x64.
   inline void MaybeOSR();
+  inline void AssertOSREmpty();
+#endif
 
   inline bool supports_f16_mem_access();
 
diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc
index 91b5892..5b178d9 100644
--- a/src/wasm/baseline/liftoff-compiler.cc
+++ b/src/wasm/baseline/liftoff-compiler.cc
@@ -1556,6 +1556,12 @@
   V8_NOINLINE void EmitDebuggingInfo(FullDecoder* decoder, WasmOpcode opcode) {
     DCHECK(for_debugging_);
 
+#if V8_TARGET_ARCH_X64
+    // If OSR is triggered during a builtin call, it must be consumed by
+    // {MaybeOSR} before the next instruction.
+    __ AssertOSREmpty();
+#endif
+
     if (V8_UNLIKELY(v8_flags.wasm_code_coverage && (opcode != kExprLoop))) {
       // Coverage instrumention for 'loop' instructions is emitted in Loop().
       EmitCoverageInstrumentationIfReachable(decoder, opcode);
@@ -4649,6 +4655,7 @@
     __ CallBuiltin(Builtin::kWasmMemoryGrow);
     DefineSafepoint();
     RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
+    MaybeOSR();
 
     if (kReturnRegister0 != result.gp()) {
       __ Move(result.gp(), kReturnRegister0, kI32);
@@ -6531,6 +6538,7 @@
     RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
 
     __ PushRegister(kI32, LiftoffRegister(kReturnRegister0));
+    MaybeOSR();
   }
 
   void AtomicNotify(FullDecoder* decoder, const MemoryAccessImmediate& imm) {
@@ -6956,6 +6964,7 @@
          VarState{kRef, LiftoffRegister{kReturnRegister0}, 0}},
         decoder->position());
     __ PushRegister(kI32, LiftoffRegister{kReturnRegister0});
+    MaybeOSR();
   }
 
   void WaitqueueNotify(FullDecoder* decoder, const Value& waitqueue,
@@ -7649,6 +7658,7 @@
         decoder->position());
 
     RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
+    MaybeOSR();
   }
 
   void TableGrow(FullDecoder* decoder, const TableIndexImmediate& imm,
@@ -7672,6 +7682,7 @@
                 decoder->position());
 
     RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
+    MaybeOSR();
     __ SmiToInt32(kReturnRegister0);
     if (imm.table->is_table64()) {
       LiftoffRegister result64 = LiftoffRegister(kReturnRegister0);
@@ -7995,6 +8006,7 @@
                    __ cache_state()->stack_state.end()[-1],  // length
                    VarState{kI32, elem_size, 0}},
                   decoder->position());
+      MaybeOSR();
     }
 
     LiftoffRegister obj(kReturnRegister0);
@@ -8250,6 +8262,7 @@
                 {VarState{kRef, rtt, 0}, VarState{kI32, elem_count, 0},
                  VarState{kI32, value_kind_size(elem_kind), 0}},
                 decoder->position());
+    MaybeOSR();
 
     // Initialize the array with stack arguments.
     LiftoffRegister array(kReturnRegister0);
@@ -8418,6 +8431,8 @@
 
     // Pop parameters from the value stack.
     __ DropValues(2);
+
+    MaybeOSR();
     RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
 
     LiftoffRegister result(kReturnRegister0);
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.