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

Abierto
#1,893 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
51/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
c

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
C
Estrellas
3.9k
Forks
389
Merge medio
4 d 5 h
PR fusionados (30 d)
16

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de quickjs-ng/quickjs

Todos los issues de quickjs-ng/quickjs

Issues similares

Más issues de C

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.