High chrome Type Confusion 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType confusion in V8
DescriptionType confusion in V8
ComponentV8
Bug ClassType Confusion
Tracker552342545
Fix commit50a6c0c69c13 (v8/v8) +2/-3
CISA KEVNot listed
CreditedTech Division (@taiphung) - Mobifone Digital Payment
Disclosed2026-09-08

Background

`Array.prototype.flat`
a JavaScript builtin that produces a new array with nested sub-array elements recursively concatenated up to a specified depth.
Torque (`.tq`)
V8’s typed domain-specific language for implementing builtins, where type casts like Cast<T> are checked and UnsafeCast<T> are not.
`Smi`
a “small integer,” V8’s tagged-pointer representation of an integer that fits inline in a machine word without a heap allocation, as opposed to a HeapNumber.
`FastJSArray`
an array whose backing store uses a fast, packed elements kind, generally assumed to carry a Smi length.

Root Cause Analysis

The fast-path loop in src/builtins/array-flat.tq read the source array’s length into sourceLength and then narrowed it to a Smi via UnsafeCast<Smi>(sourceLength), guarded only by a dcheck(Is<Smi>(sourceLength)). The code justified this with the comment that a FastJSArray “length must be a Smi,” but a dcheck is compiled out of release builds, so nothing at runtime enforced the invariant. If sourceLength was actually a HeapNumber rather than a Smi, UnsafeCast<Smi> reinterpreted the boxed-double pointer as a tagged small integer, yielding a corrupted smiSourceLength used as the loop bound over fastSource.

The fix replaces the unchecked narrowing with Cast<Smi>(sourceLength) otherwise goto Bailout(...), so a non-Smi length now safely diverts to the generic slow path instead of being misinterpreted.

Key insight
The single mistake was trusting a debug-only dcheck invariant to make an UnsafeCast<Smi> sound at runtime; the fix replaces it with a real Cast<Smi> whose failure branch bails to the safe slow path.

Attack Path

  1. Shape an array with a non-`Smi` length Arrange for a FastJSArray reaching the flat fast path to hold its length as a HeapNumber rather than a Smi, violating the assumed invariant.
  2. Invoke `Array.prototype.flat` Call flat so execution enters the fast-path loop in array-flat.tq and reaches the UnsafeCast<Smi>(sourceLength).
  3. Trigger the misinterpretation In a release build the dcheck is absent, so the HeapNumber pointer is reinterpreted as a Smi, producing a bogus smiSourceLength bound.
  4. Drive confused iteration The corrupted smiSourceLength controls the for (; smiSourceIndex < smiSourceLength; ...) loop over fastSource, mixing a type-confused value into array traversal.

Impact Assessment

An attacker running JavaScript in the renderer’s V8 sandbox obtains a type confusion between a Smi and a HeapNumber used as an array-length loop bound, which can lead to out-of-bounds behavior over the fast array’s elements. Exploitation requires only that untrusted script reach the Array.prototype.flat fast path with a source array whose length is not a Smi, and takes effect only in release builds where the dcheck guard is compiled out.

Changed Functions

FunctionChangeNotes
for
src/builtins/array-flat.tq
modified

Files Changed

  • src/builtins/array-flat.tq

Audit Directions

  • `UnsafeCast` justified by `dcheck`
    Flag any UnsafeCast<T> in Torque whose safety rests on an adjacent dcheck/Is<T> assumption, since that check vanishes in release builds; prefer a checked Cast<T> otherwise goto bailout.
  • Assumed `Smi` invariants on array lengths
    Audit fast-path builtins that treat a FastJSArray or similar length as guaranteed Smi without a runtime narrowing check.
  • Loop bounds from unchecked casts
    Review loops whose iteration bound is produced by an unchecked type narrowing, as a confused bound can drive out-of-bounds element access.
From 50a6c0c69c1384720d9c3599dadfa73e55b865d7 Mon Sep 17 00:00:00 2001
From: Igor Sheludko <ishell@chromium.org>
Date: Wed, 02 Sep 2026 13:14:21 +0200
Subject: [PATCH] [builtins] Fix Array.prototype.flat

This CL replaces the unsafe cast with a safe Cast<Smi>, branching to
the slow path when sourceLength is not a Smi.

TAG=agy
CONV=1183a007-e898-453b-9823-1c970699f025

Fixed: 552342545
Change-Id: I539dd7006dfece6c247565a0940aeac9d7cc669a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8346487
Auto-Submit: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Patrick Thier <pthier@chromium.org>
Commit-Queue: Patrick Thier <pthier@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#109636}
---

diff --git a/src/builtins/array-flat.tq b/src/builtins/array-flat.tq
index a40b8a0..659de78 100644
--- a/src/builtins/array-flat.tq
+++ b/src/builtins/array-flat.tq
@@ -420,9 +420,8 @@
       otherwise goto Bailout(targetIndex, smiSourceIndex);
   let fastOW = NewFastJSArrayWitness(fastSource);
 
-  // The source is a FastJSArray, thus its length must be a Smi.
-  dcheck(Is<Smi>(sourceLength));
-  const smiSourceLength = UnsafeCast<Smi>(sourceLength);
+  const smiSourceLength = Cast<Smi>(sourceLength)
+      otherwise goto Bailout(targetIndex, smiSourceIndex);
 
   // 3. Repeat, while sourceIndex < sourceLen
   for (; smiSourceIndex < smiSourceLength; smiSourceIndex++) {
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.