Memory leak: a property key made from a slice of a large string leaks the parent string on every lookup

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

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

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

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
51/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
活発
技術スタック
c

調査の方向性

The report points to quickjs.c and gives a JavaScript reproduction that triggers the leak under AddressSanitizer. Start by tracing slice strings as they become property key atoms and are freed, focusing on the reported paths around js_string_repeat and JS_FreeAtomStruct. Reproduce with leak detection enabled and confirm the parent string is released after repeated lookups.

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

説明

My agent encountered this tangentially when fuzzing quickjab. Reproduces on 3 boxes, Ubuntu and Alpine.
Agentic below

Version: quickjs-ng master at e22ce4d; also v0.16.2.
Build: clang 21, -fsanitize=address, Debug, Linux x86_64.

Repro:
const L = {};
for (let i = 0; i < 100; i++) L[("x".repeat(5000) + i).slice(4)];
$ ASAN_OPTIONS=detect_leaks=1 ./qjs leak.js
SUMMARY: AddressSanitizer: 505090 byte(s) leaked in 100 allocation(s).
Each leaked block is the parent string allocated in js_string_repeat:
#0 malloc
#1 arena_malloc_large quickjs.c:1783
#2 js_malloc_rt quickjs.c:2021
#3 js_alloc_string_rt quickjs.c:2579
#4 js_alloc_string quickjs.c:2598
#5 string_buffer_init2 quickjs.c:4380
#6 js_string_repeat quickjs.c:48048

Observations:

  • substring() leaks the same way as slice().
  • The leak grows linearly with the number of lookups, so any script can grow memory without bound.
  • There is no leak when the key is the large string itself (L["x".repeat(5000)]), or when the parent is short (300 chars), presumably because no JS_STRING_KIND_SLICE string is created then.

Suspected cause (not verified):

  1. When a property key is Atom reuses the string in place if str->atom_type= atom_type; (around quickjs.c:3545). Its kind stays JS_STRING_KIND_SLICE.
  2. When that atom is later freed, js_free_string0 takes the str->atom_type branch and calls JS_FreeAtomStruct. Only the non-atom branch handles JS_STRING_KIND_SLICE by releasing slice->parent, so the parent's reference is never dropped.

Possible fixes:

  • Flatten slice (and indirect) strings to JS_STRING_KIND_NORMAL before reusing them as atoms, as the atom_type != 0 copy path already does.
  • Or release slice->parent in JS_FreeAtomStruct.

Found by: fuzzing an embed + ASan.

主要言語
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 を短くまとめたダイジェスト。