CVE-2025-13630
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/maglev/maglev-graph-builder.cc |
modified |
Files Changed
src/maglev/maglev-graph-builder.cc
Patch
From 00348ef5c3c11842465ca11e7e39706dbe91ed86 Mon Sep 17 00:00:00 2001 From: Marja Hölttä <[email protected]> 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ä <[email protected]> Reviewed-by: Victor Gomes <[email protected]> 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 {};
Original Bug Report
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:
-
The outer loop calls
f5([f5])repeatedly, where the receiver is an array of functions withPACKED_ELEMENTSkind. -
Inside
f5, it callsa6.slice()and then defines and invokesf9([,1.1]), which creates aHOLEY_DOUBLE_ELEMENTSarray. -
The
forEachcall on this array invokesf5recursively 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
HoleyFloat64for 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