Array iterator, Proxy ownKeys and RegExp @@replace read `length` with ToUint32 instead of ToLength

オープン
#1,890 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
57/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
c, javascript

調査の方向性

Start by reading js_array_iterator_next, js_proxy_get_own_property_names, js_regexp_Symbol_replace, and js_string_GetSubstitution, which the issue identifies as using js_get_length32(). Compare with the js_get_length64() change in #1816 and the relevant ECMAScript length rules. Done means the reported negative and above-2^32 cases follow ToLength semantics, with limits handled without wrapping; run the reproductions and the related tests.

索引モデルが issue の本文から書いたものです。

説明

Environment
  • QuickJS-ng v0.18.0 (d7215b0) and master e22ce4d (2026-11-02), built with make, Debian 12 x86-64, gcc 12.2
  • Also seen in v0.15.0
  • Reference: node v26.13.0
  • Checked on 2026-11-03
Reproduction
// 1. array iterator, negative length (terminates)
var n = 0;
for (var x of Array.prototype.values.call({ length: -1, 0: "a" })) { if (++n > 3) break; }
console.log(n);                       // expected 0, actual 4

// 2. array iterator, length above 2**32 wraps
n = 0;
for (var k of Array.prototype.keys.call({ length: 2 ** 32 + 1 })) { if (++n > 5) break; }
console.log(n);                       // expected 6, actual 1

// 3. Proxy ownKeys trap result with a negative length
console.log(Reflect.ownKeys(new Proxy({}, { ownKeys() { return { length: -1, 0: "a" }; } })).length);
                                      // expected 0, actual TypeError

// 4. Proxy ownKeys trap result with length 2**32 + 1
console.log(Reflect.ownKeys(new Proxy({}, { ownKeys() { return { length: 2 ** 32 + 1, 0: "a" }; } })).length);
                                      // expected TypeError (index 1 is undefined) or a RangeError limit, actual 1

These do not terminate (about 2^32 iterations), run with a timeout:

console.log([...Array.prototype.values.call({ length: -1, 0: "a" })].length);   // expected 0

var re = /x/;
re.exec = function () { re.exec = () => null; return { length: -1, 0: "x", index: 0 }; };
console.log("x".replace(re, "[$&]"));                                           // expected [x]
Expected (spec)

All of these read the length with LengthOfArrayLike, that is
ToLength(Get(obj, "length")), which clamps to [0, 2^53 − 1]: ToLength(−1) is 0, and 2^32 + 1 stays 2^32 + 1.

ToUint32 maps −1 to 2^32 − 1 and 2^32 + 1 to 1, which explains every result above.

Cause

These sites use js_get_length32() (ToUint32):

  • js_array_iterator_next
  • js_proxy_get_own_property_names
  • js_regexp_Symbol_replace (nCaptures) and js_string_GetSubstitution (captures)

A possible fix (not written or tested): read the length with js_get_length64() (ToLength), as #1816 does for
build_arg_list. Above what a site can represent, throw a RangeError instead of wrapping (ownKeys, captures), or use a
64-bit index (the array iterator).

Related
  • #1764 / #1816: the same conversion in build_arg_list (Function.prototype.apply, Reflect.apply, Reflect.construct). That PR does not touch the sites above.
  • #1609: fixed an allocation overflow in the Proxy ownKeys path. The length conversion there is still ToUint32.
  • bellard/quickjs has the same code at these sites (its apply already uses ToLength).
主要言語
C
スター
3.9k
フォーク
389
平均マージ
4日 5時間
マージ済み PR(30日)
16

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

quickjs-ng/quickjs のほかの issue

quickjs-ng/quickjs の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。