High chrome Type Confusion 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType Confusion in Runtime
DescriptionType Confusion in Runtime
ComponentRuntime
Bug ClassType Confusion
Tracker502830119
Fix commit615d074ab277 (v8/v8) +15/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Changed Functions

FunctionChangeNotes
if
src/builtins/array-slice.tq
modified

Files Changed

  • src/builtins/array-slice.tq
From 615d074ab27797662bd4c4e1fc5236a1fccce022 Mon Sep 17 00:00:00 2001
From: Marja Hölttä <marja@chromium.org>
Date: Thu, 16 Apr 2026 14:56:51 +0200
Subject: [PATCH] [builtins] Fix Array.p.slice bailout threshold on arguments objects

Fixed: 502830119
Change-Id: Ia5b299cd9c33cabfe2a0647ff6a54e1942d6cd3f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7766691
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#106550}
---

diff --git a/src/builtins/array-slice.tq b/src/builtins/array-slice.tq
index bb56285..d3f80e5 100644
--- a/src/builtins/array-slice.tq
+++ b/src/builtins/array-slice.tq
@@ -3,12 +3,21 @@
 // found in the LICENSE file.
 
 namespace array {
+
+const kJSArrayHeaderSizeSlots:
+    constexpr int31 generates 'JSArray::kHeaderSize / kTaggedSize';
+
 macro HandleSimpleArgumentsSlice(
     context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi,
     count: Smi): JSArray
     labels Bailout {
   // If the resulting array doesn't fit in new space, use the slow path.
-  if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
+  // We subtract the JSArray header size to ensure the JSArray and elements
+  // allocation can be safely folded together into a single object.
+  if (count >= kMaxNewSpaceFixedArrayElements - kJSArrayHeaderSizeSlots) {
+    goto Bailout;
+  }
+
 
   const end: Smi = start + count;
   const sourceElements: FixedArray =
@@ -32,7 +41,11 @@
     count: Smi): JSArray
     labels Bailout {
   // If the resulting array doesn't fit in new space, use the slow path.
-  if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
+  // We subtract the JSArray header size to ensure the JSArray and elements
+  // allocation can be safely folded together into a single object.
+  if (count >= kMaxNewSpaceFixedArrayElements - kJSArrayHeaderSizeSlots) {
+    goto Bailout;
+  }
 
   const sloppyElements: SloppyArgumentsElements =
       Cast<SloppyArgumentsElements>(args.elements) otherwise Bailout;
Loading diff…

Original Bug Report

reported by rj...@google.com

Missing write barrier in Array slice for sloppy arguments due to allocation folding threshold mismatch

Project Fortify has identified a security issue and generated a PoC.

d8 variant: ‘Default’

flags: –allow-natives-syntax –expose-gc –no-inline-new –scavenger-chaos-mode –scavenger-chaos-mode-threshold=90 –verify-heap –omit-quit –fuzzing

Return code: 134

<details>

<summary>stdout</summary>

Starting test...
Attempt 0

</details>

<details>

<summary>stderr</summary>



#
# Fatal error in ../../v8/src/heap/heap-verifier.cc, line 566
# Check failed: untyped_->count(slot.address()) > 0 (0 vs. 0).
#
#
#
#FailureMessage Object: 0x7ffe3ae8a9e0
==== C stack trace ===============================

    /compressed/bin/Default/libv8_libbase.so(v8::base::debug::StackTrace::StackTrace()+0x1e) [0x7fb2375f489e]
    /compressed/bin/Default/libv8_libplatform.so(+0x163cd) [0x7fb23d4d43cd]
    /compressed/bin/Default/libv8_libbase.so(V8_Fatal(char const*, int, char const*, ...)+0x194) [0x7fb2375d6b14]
    /compressed/bin/Default/libv8.so(+0x26fb1e5) [0x7fb239cfb1e5]
    /compressed/bin/Default/libv8.so(+0x2729a96) [0x7fb239d29a96]
    /compressed/bin/Default/libv8.so(+0x26f86f4) [0x7fb239cf86f4]
    /compressed/bin/Default/libv8.so(v8::internal::PagedSpaceBase::Verify(v8::internal::Isolate*, v8::internal::SpaceVerificationVisitor*) const+0x156) [0x7fb239ec3176]
    /compressed/bin/Default/libv8.so(+0x26f7412) [0x7fb239cf7412]
    /compressed/bin/Default/libv8.so(v8::internal::HeapVerifier::VerifyHeap(v8::internal::Heap*)+0x87) [0x7fb239cf8e77]
    /compressed/bin/Default/libv8.so(+0x276efbd) [0x7fb239d6efbd]
    /compressed/bin/Default/libv8.so(+0x276df86) [0x7fb239d6df86]
    /compressed/bin/Default/libv8.so(+0x27ad1a2) [0x7fb239dad1a2]
    /compressed/bin/Default/libv8.so(+0x27acd0f) [0x7fb239dacd0f]
    /compressed/bin/Default/libv8.so(+0x3da4d4b) [0x7fb23b3a4d4b]
    /compressed/bin/Default/libv8.so(v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags, v8::internal::PerformHeapLimitCheck, v8::internal::PerformIneffectiveMarkCompactCheck)+0x473) [0x7fb239d69b93]
    /compressed/bin/Default/libv8.so(+0x2579c9e) [0x7fb239b79c9e]
    /compressed/bin/Default/libv8.so(+0x25797f4) [0x7fb239b797f4]
    /compressed/bin/Default/libv8.so(+0xf94930) [0x7fb238594930]
Received signal 6
Aborted (core dumped)

</details>

Overview: A size threshold mismatch between Torque and the CodeStubAssembler allows certain JSArray allocations to be unfolded despite passing safety checks. This unfolded allocation can trigger a garbage collection that promotes the array elements to old space, while elements are copied into it without a write barrier. This results in stale tagged pointers and V8 heap type confusion.

Affected files:

  • v8/src/builtins/array-slice.tq
  • v8/src/codegen/code-stub-assembler.cc

Estimated timestamp from git blame: 2025-03-31

Root Cause

In v8/src/builtins/array-slice.tq, the HandleFastAliasedSloppyArgumentsSlice macro attempts to optimize Array.prototype.slice on fast-aliased sloppy arguments. It guards the fast path by checking count >= kMaxNewSpaceFixedArrayElements (which resolves to FixedArray::kMaxRegularLength, typically 32766 on pointer-compressed builds). If the check passes, it allocates a new JSArray and copies elements into its backing store using UNSAFE_SKIP_WRITE_BARRIER.

The use of UNSAFE_SKIP_WRITE_BARRIER relies on the assumption that the JSArray header and its FixedArray elements will be allocated as a single contiguous “folded” block in new-space, guaranteeing the elements array cannot be in old-space.

However, in CodeStubAssembler::AllocateUninitializedJSArrayWithElements, the decision to fold the allocation is based on whether the combined size of the JSArray header and the FixedArray elements is within kMaxRegularHeapObjectSize (128 KB). For a count value of 32763, the FixedArray alone fits within kMaxRegularHeapObjectSize (so it passes the Torque guard), but the combined size including the 16-byte JSArray header exceeds 131072 bytes.

Because the combined size is too large, the CSA performs an unfolded allocation: it allocates the FixedArray elements first, and then allocates the JSArray header separately. If the new-space is near capacity, the second allocation will trigger a scavenge garbage collection. During this scavenge, the just-allocated FixedArray can be promoted to the old generation.

When execution returns to Torque, it writes young-generation mapped parameters into this now old-generation FixedArray using UNSAFE_SKIP_WRITE_BARRIER. This old-to-new pointer is never recorded in the remembered set. Upon the next minor GC, the young objects are collected, leaving dangling pointers inside the elements array and leading to highly exploitable V8 heap type confusion.

Suggested Fix

There are two potential fixes for this issue:

  1. Use standard write barriers: The most robust fix is to remove UNSAFE_SKIP_WRITE_BARRIER and use the default StoreFixedArrayElement which emits proper write barriers. The performance overhead in this specific slice path is likely negligible.
  2. Adjust the bailout threshold: If the barrier-skip optimization is strictly required, the bailout threshold in HandleFastAliasedSloppyArgumentsSlice must be tightened to account for the JSArray header size (e.g., subtracting JSArray::kHeaderSize / kTaggedSize from the maximum capacity check) to guarantee the allocation will be safely folded.

Evaluated with Chrome root at commit: c0eb5541aebfa4ea08806eaf6e94bcc69f87ab2f


The description of the vuln is LLM-generated and can contain mistakes. Your feedback is appreciated, and will help us make improvement over time. The PoC was run in a VM and it seemed to be legit - if not, let us know and we can strengthen our checker.

View on issue tracker