Chrome · V8
CVE-2026-17989
Type Confusion in V8
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
src/base/macros.hsrc/codegen/bailout-reason.hsrc/wasm/baseline/arm/liftoff-assembler-arm-inl.hsrc/wasm/baseline/arm64/liftoff-assembler-arm64-inl.hsrc/wasm/baseline/ia32/liftoff-assembler-ia32-inl.hsrc/wasm/baseline/liftoff-assembler.hsrc/wasm/baseline/liftoff-compiler.cc
Patch
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.
References
On This Page