High chrome Type Confusion 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType Confusion in V8
DescriptionType Confusion in V8
ComponentV8
Bug ClassType Confusion
Tracker456547591
Fix commit00348ef5c3c1 (v8/v8) +4/-0
CISA KEVNot listed
CreditedShreyas Penkar (@streypaws)
Disclosed2025-12-02

Changed Functions

FunctionChangeNotes
if
src/maglev/maglev-graph-builder.cc
modified

Files Changed

  • src/maglev/maglev-graph-builder.cc
From 00348ef5c3c11842465ca11e7e39706dbe91ed86 Mon Sep 17 00:00:00 2001
From: Marja Hölttä <marja@chromium.org>
Date: Fri, 31 Oct 2025 12:34:52 +0100
Subject: [PATCH] [maglev] Fix a bug in reducing Array.p.slice

We might be in a polymorphic branch where the receiver is surely
not a JSReceiver (although we don't figure it out based on the
possible maps).

Fixed: 456547591
Change-Id: I2dc6b92ec9c537d72543b570c3e26bd1112c7f3f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7106059
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Victor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/main@{#103441}
---

diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index 0082441..ecd22cc 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -9204,6 +9204,10 @@
     return {};
   }
 
+  if (!CheckType(receiver, NodeType::kJSReceiver)) {
+    return {};
+  }
+
   auto possible_maps = known_node_aspects().TryGetPossibleMaps(receiver);
   if (!possible_maps) {
     return {};
Loading diff…

Original Bug Report

reported by sh...@gmail.com

Maglev - CallBuiltin (input @0 = LoadHoleyFixedDoubleArrayElement) type HoleyFloat64 is not Tagged

VERSION

V8 Version: commit hash a3d368877487647fbd9d6dca8de88c3762ab2793 (ToT)

Operating System: Linux

REPRODUCTION CASE

Please run the following JS snippet using debug version of v8 with --jit-fuzzing and --turbolev flags. With these 2 flags, the crash appears sometimes. If we add the flag --turboshaft-assert-types the crash can be triggered reliably.

for (let i1 = 100; i1; --i1) {
}
function f5(a6) {
    for (let v7 = 0; v7 < 5; v7++) {
    }
    a6.slice(); 
    function f9(a10) {
        a10.forEach(f5);  
        return a10;
    }
    f9([,1.1]);
    return f5;
}
for (let v16 = 0; v16 < 25; v16++) {
    const v17 = [f5];
    try { f5(v17); } catch (e) {}
}

It will produce the following crash (in debug build) -

#
# Fatal error in ../../src/maglev/maglev-ir.cc, line 630
# Type representation error: node #95 : CallBuiltin (input @0 = LoadHoleyFixedDoubleArrayElement) type HoleyFloat64 is not Tagged
#
#
#
#FailureMessage Object: 0x7f8db1b655c8
==== C stack trace ===============================

    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8_libbase.so(v8::base::debug::StackTrace::StackTrace()+0x1e) [0x7f8dbf4e76ce]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8_libplatform.so(+0x4ffcd) [0x7f8dbf449fcd]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8_libbase.so(V8_Fatal(char const*, int, char const*, ...)+0x205) [0x7f8dbf4bcb85]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::maglev::CheckValueInputIs(v8::internal::maglev::NodeBase const*, int, v8::internal::maglev::ValueRepresentation)+0x207) [0x7f8dc9ba59c7]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::maglev::CallBuiltin::VerifyInputs() const+0x1ee) [0x7f8dc9ba676e]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::maglev::ProcessResult v8::internal::maglev::MaglevGraphVerifier::Process<v8::internal::maglev::CallBuiltin>(v8::internal::maglev::CallBuiltin*, v8::internal::maglev::ProcessingState const&)+0x1b5) [0x7f8dc9620185]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::maglev::GraphProcessor<v8::internal::maglev::MaglevGraphVerifier>::ProcessNodeBase(v8::internal::maglev::NodeBase*, v8::internal::maglev::ProcessingState const&)+0x11b7) [0x7f8dc9612c67]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::maglev::GraphProcessor<v8::internal::maglev::MaglevGraphVerifier>::ProcessGraph(v8::internal::maglev::Graph*)+0x446) [0x7f8dc96108a6]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::compiler::turboshaft::RunMaglevOptimizations(v8::internal::compiler::turboshaft::PipelineData*, v8::internal::maglev::MaglevCompilationInfo*, v8::internal::maglev::Graph*)+0x2f5) [0x7f8dcbae3895]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::compiler::turboshaft::TurbolevGraphBuildingPhase::Run(v8::internal::compiler::turboshaft::PipelineData*, v8::internal::Zone*, v8::internal::compiler::Linkage*)+0x3de) [0x7f8dcbb4d27e]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(auto v8::internal::compiler::turboshaft::Pipeline::Run<v8::internal::compiler::turboshaft::TurbolevGraphBuildingPhase, v8::internal::compiler::Linkage*&>(v8::internal::compiler::Linkage*&)+0x1ce) [0x7f8dcaab767e]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::compiler::turboshaft::Pipeline::CreateGraphWithMaglev(v8::internal::compiler::Linkage*)+0xa4) [0x7f8dcaaabc54]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::compiler::PipelineCompilationJob::ExecuteJobImpl(v8::internal::RuntimeCallStats*, v8::internal::LocalIsolate*)+0x1ab) [0x7f8dcaa97b0b]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::OptimizedCompilationJob::ExecuteJob(v8::internal::RuntimeCallStats*, v8::internal::LocalIsolate*)+0x128) [0x7f8dc7e1b028]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::OptimizingCompileTaskExecutor::RunCompilationJob(v8::internal::OptimizingCompileTaskState&, v8::internal::Isolate*, v8::internal::LocalIsolate&, v8::internal::TurbofanCompilationJob*)+0x23c) [0x7f8dc7f8411c]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8.so(v8::internal::OptimizingCompileTaskExecutor::CompileTask::Run(v8::JobDelegate*)+0x35b) [0x7f8dc7f8c49b]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8_libplatform.so(v8::platform::DefaultJobWorker::Run()+0xbe) [0x7f8dbf44899e]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8_libplatform.so(v8::platform::DefaultWorkerThreadsTaskRunner::WorkerThread::Run()+0xac) [0x7f8dbf4500fc]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8_libbase.so(v8::base::Thread::NotifyStartedAndRun()+0x32) [0x7f8dbf4e6dd2]
    /home/br4v3h3r0/v8/v8/out/x64.debug/libv8_libbase.so(+0x7b41a) [0x7f8dbf4e541a]
    /lib/x86_64-linux-gnu/libpthread.so.0(+0x8609) [0x7f8dbeede609]
    /lib/x86_64-linux-gnu/libc.so.6(clone+0x43) [0x7f8dbecb4353]
Trace/breakpoint trap

It is important to note that this crash is only reproducible in the debug version of V8.

Bisect

After bisect it seems that the bug was introduced in the following commit -

[maglev, turbolev] Implement array.prototype.slice -> [ebcd571a1d1] Revision: 101590

https://chromium.googlesource.com/v8/v8/+/ebcd571a1d1%5E%21/#F2

Analysis

The crash seems to occur due to a type representation mismatch in Maglev’s intermediate representation. The error message indicates that a CallBuiltin node for Builtin::kCloneFastJSArray is receiving an input with HoleyFloat64 representation when it expects Tagged representation.

During graph verification, CallBuiltin::VerifyInputs() validates that all inputs match the expected types specified by the builtin’s call interface descriptor. The CloneFastJSArray builtin expects its source parameter to be MachineType::AnyTagged(), which corresponds to ValueRepresentation::kTagged in Maglev. However, the actual input is a LoadHoleyFixedDoubleArrayElement node, which produces ValueRepresentation::kHoleyFloat64 - a special representation for loading double values from holey arrays that can contain holes.

The fundamental issue is that the optimization incorrectly passes a non-tagged value (an array element loaded from a double array) where a tagged object (the array itself) is expected. This happens because the receiver value somehow becomes confused with or replaced by an element loaded from an array during optimization passes.

Bisect Analysis

The bug was introduced in commit ebcd571a1d1c593ef03d18445a2573ec6c92c66d which implemented the TryReduceArrayPrototypeSlice optimization for Maglev.

The implementation follows a similar pattern to other array builtin optimizations like TryReduceArrayPrototypeAt, but seems to contain a critical missing validation step. While it checks that the receiver has possible maps that support fast array iteration and validates the start/end parameters, it fails to verify that the receiver itself has the correct type representation before passing it to BuildCallBuiltin.

Other similar optimization functions in the same file, such as TryReduceArrayPrototypeEntries, include an explicit check to ensure the receiver is a JSReceiver before proceeding. This check is absent from TryReduceArrayPrototypeSlice, allowing the optimization to proceed even when the receiver has an incompatible representation.

PoC Analysis

The PoC creates a scenario that triggers the bug through complex control flow involving inlining, nested function calls, and polymorphic receiver types:

  1. The outer loop calls f5([f5]) repeatedly, where the receiver is an array of functions with PACKED_ELEMENTS kind.

  2. Inside f5, it calls a6.slice() and then defines and invokes f9([,1.1]), which creates a HOLEY_DOUBLE_ELEMENTS array.

  3. The forEach call on this array invokes f5 recursively with each element: first with undefined (converted from the hole), then with the double value 1.1.

During optimization, when Maglev tries to inline these calls, it encounters multiple call sites to f5 with different receiver types. The compiler must create phi nodes to merge control flow paths where the receiver could be:

  • The original array [f5] (Tagged, PACKED_ELEMENTS)
  • Elements from the double array (potentially HoleyFloat64 for double elements)

The bug manifests when a phi node or value numbering incorrectly merges or aliases the receiver parameter with a LoadHoleyFixedDoubleArrayElement node from an inlined forEach iteration. When TryReduceArrayPrototypeSlice attempts to optimize the a6.slice() call, it receives this incorrectly-typed value node and passes it directly to BuildCallBuiltin<Builtin::kCloneFastJSArray>({receiver}) without validating its representation. The verification phase then catches this type error when CallBuiltin::VerifyInputs() discovers that input @0 has HoleyFloat64 representation instead of the required Tagged representation.

Possible Fix

The fix could add an explicit type check for the receiver at the beginning of TryReduceArrayPrototypeSlice, following the established pattern used by other array prototype optimizations in the same file.

Specifically, after obtaining the receiver value, the function should call CheckType(receiver, NodeType::kJSReceiver) and return early if the check fails.

Reporter Credit: @streypaws

View on issue tracker