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

Đang mở
#1,893 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
51/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.

Ngôn ngữ chính
C
Star
3.9k
Fork
389
Merge trung bình
4 ngày 5 giờ
Pull request đã merge (30 ngày)
16

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của quickjs-ng/quickjs

Tất cả issue của quickjs-ng/quickjs

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.