Skip to content

Merge quickjs-ng v0.15.1 (explicit resource management, ECO-382) - #8

Merged
syrusakbary merged 33 commits into
masterfrom
eco-382-bump-quickjs-ng-0.15.1
Jul 22, 2026
Merged

Merge quickjs-ng v0.15.1 (explicit resource management, ECO-382)#8
syrusakbary merged 33 commits into
masterfrom
eco-382-bump-quickjs-ng-0.15.1

Conversation

@Arshia001

Copy link
Copy Markdown
Member

Bumps the vendored engine v0.14.0 → v0.15.1, primarily to gain explicit resource management (using / await using, upstream #1458) — the fix for ECO-382 — plus the v0.15.x bug fixes (notably the build_backtrace heap-UAF fix). Also broadens general QuickJS↔V8 parity.

Merge conflicts

Exactly 2 hunks in one file (quickjs.c, build_backtrace): our V8-compatible stack-trace patch (lazy stack getter/setter, stack_property_defined, callsites) vs upstream's own build_backtrace use-after-free fix.

Resolution: adopt upstream's UAF fix — dup error_val into a held error_obj (freed at function exit) and use error_obj for every object-touching site in the trailing section — while preserving our lazy getter/setter feature. Our carried async-generator cycle-GC UAF fix merged without conflict.

Verification (local)

  • quickjs.c compiles with zero errors
  • tests/test_language.js (incl. the new explicit-resource-management suite) passes
  • using / await using dispose in correct LIFO order (x,B,A)
  • tests/test_closure.js passes (exercises the carried closure/GC patch)
  • The pre-existing standalone-CLI --std segfault is unchanged from pre-merge fork HEAD and does not affect the embedded (edgejs) engine

Follow-up (not in this PR)

After CI is green here: bump the napi submodule pointer, then the edgejs pointer, and drop QUICKJS_SKIP_USING_PARSER_TESTS (re-enabling the 4 stream dispose tests).

CI to watch here: ASAN+UBSAN + test262 (runs on PR), Valgrind/TSAN (on merge to master).

bnoordhuis and others added 30 commits May 7, 2026 09:58
Add a new JS_ABORT_ON_LEAKS flag for use in api-test.c to help catch
regressions.
Co-authored-by: Ben Noordhuis <info@bnoordhuis.nl>

Closes: quickjs-ng#865
The bytecode format is not hardened against a hostile producer, and the
SAB transport in JS_Write/ReadObject embeds a raw host pointer that the
reader will dereference verbatim. Both expectations are project lore;
embedders only see the flag definitions in quickjs.h. Document them at
the API boundary.

Docs-only change, no test.
The retry loop in js_waker_signal used `errno != EAGAIN || errno != EINTR`,
which is always true (any single errno value is unequal to at least one of
the two), so the loop bailed on the first short-write error and silently
dropped the wakeup. Replace with `&&` so the loop only exits on a fatal
errno.

No test: js_waker_signal's transient-error path is reached only when the
pipe is full or the syscall is interrupted by a signal, neither of which
is reliably reproducible in a portable unit test.

Co-authored-by: Claude <noreply@anthropic.com>
The C API took a size_t len but passed it to js_alloc_string, whose
length parameter is int. With len > INT_MAX (e.g. INT_MAX + 1), the
cast truncated the value, producing either a tiny or negative-sized
allocation while the subsequent memcpy(str16(str), buf, len * 2)
wrote the full size_t length — heap overflow on misuse from C.

Reject len > JS_STRING_LEN_MAX before allocating, matching the
existing guard in JS_NewStringLen.

Test: api-test now calls JS_NewStringUTF16(ctx, NULL, INT_MAX + 1)
and asserts JS_IsException + the "invalid string length" error.
Before the fix, the same call segfaults (or is caught by ASan as a
heap-buffer-overflow).
…nt_exception

If JS_NewError() during build_backtrace triggered dbuf OOM, JS_ThrowOutOfMemory
freed the current exception (error_val from the caller's stack frame), then
the rest of build_backtrace continued using the freed error_val for the
prepareStackTrace call and the JS_DefinePropertyValue of the stack property.

The fix duplicates error_val into a local error_obj at function entry, uses
error_obj throughout the function, and frees it at exit.

Fixes quickjs-ng#1469
In a sentence "The `test262` suite is also ran," 'run' should be used instead of 'ran'. It is the past participle in the sentence, despite 'is' being in the present tense. 'run' is irregular, but if you substitute a regular verb like 'execute', it becomes more clear:

"The `test262` suite is also executed." If the present participle were used, it would be 'executing'.

It's a common mistake in English, even amongst native speakers and linguistic drift may eventually put this in free variation, but for now 'run' is correct.
Also make help return success.
Bump the vendored engine from v0.14.0 to v0.15.1 to gain explicit
resource management (`using` / `await using`, PR quickjs-ng#1458) plus the
v0.15.x bug fixes (notably the build_backtrace heap-UAF fix), toward
closing ECO-382 and broadening QuickJS feature parity with V8.

Conflict resolution (quickjs.c `build_backtrace` only, 2 hunks):
adopt upstream's use-after-free fix — dup `error_val` into a held
`error_obj` and free it at function exit — while preserving our
V8-compatible lazy `stack` getter/setter (`stack_property_defined`)
and callsite machinery. All object-touching sites in the trailing
section now use the held `error_obj`.

Verified: quickjs.c compiles clean; tests/test_language.js (incl. the
new explicit-resource-management suite) passes; `using`/`await using`
dispose in correct LIFO order; our carried async-generator cycle-GC
UAF fix merged without conflict and test_closure.js passes. The
pre-existing standalone-CLI `--std` segfault is unchanged from the
pre-merge fork HEAD and does not affect the embedded (edgejs) engine.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arshia001 and others added 3 commits July 21, 2026 16:43
Enabling the fork's inherited CI (previously gated) ran the asan+ubsan
job against our downstream patches for the first time. Two latent
UBSAN "member access within null pointer" findings, both pre-dating the
v0.15.1 bump, failed the `run-test262 -c tests.conf` test step:

- js_generator_next: `sf = &s->func_state->frame` was formed before the
  state switch, but our async-generator cycle-GC UAF fix made
  `func_state` a heap pointer that is NULL in the AWAITING_RETURN and
  COMPLETED states (upstream embeds it by value). `sf` is only
  dereferenced in the suspended-yield paths where func_state is live, so
  only form the frame pointer when it is non-NULL.

- js_create_module_function: recursed into `rme->module` for every
  dependency, but that stays NULL for a not-yet-resolved dependency.
  Skip null dependency modules (they are instantiated when they
  resolve) instead of recursing with a null module.

Also neuter the function-source-position asserts in tests/test_builtin.js
(in place, preserving line numbers used by neighbouring source-position
tests): function.lineNumber/columnNumber are intentionally removed in
this fork for V8 compatibility, so those asserts do not apply.

`run-test262 -c tests.conf` now passes clean (0/63 errors) under
ASAN+UBSAN with leak detection; closed-generator next/return/throw,
async-generator GC stress, and build_backtrace stress all verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upstream quickjs-ng runs a ~100-job matrix (every Windows toolchain, a
meson x OS x sanitizer x allocator grid, riscv64, cygwin, tcc, msan,
mimalloc, docs, release, ...). This fork's only consumer is edgejs,
which ships native builds on Linux and macOS and a WASIX build via
wasmer, so the vast majority of that matrix is permanently-red noise
that drowns the signal.

Reduce ci.yml to 5 jobs: Debug + Release + ASAN+UBSAN(+test262) on
Linux, and Debug + ASAN+UBSAN(+test262) on macOS — build/test/test262/
api/lre via the same make targets as before. Drop the codegen job (our
committed generated files diverge from upstream's generators; freshness
is a separate concern).

Delete the fork-irrelevant release/docs/test-docs workflows, and gate
tsan/valgrind to workflow_dispatch so they stay available on demand
without turning master red on pre-existing findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enabling the fork CI surfaced two more classes of intentional
divergence between this downstream engine and upstream quickjs-ng's own
test suites, unrelated to the v0.15.1 bump:

- `./build/api-test` (C API conformance) asserts upstream semantics we
  deliberately changed: compile-time module import resolution (this fork
  defers, V8-style) and WTF-8 lone-surrogate encoding, among others.
  edgejs exercises the engine through its own napi test suite, so drop
  this step rather than carry a forked api-test.c.

- The full test262 suite needs test262.conf curated for this fork's
  intentional divergences (removed function.lineNumber/columnNumber,
  deferred module loading) before it can be green; tracked as follow-up.
  `make test` (the bundled tests.conf) still runs as the conformance
  gate and passes clean under ASAN+UBSAN.

All five jobs (Debug/Release/asan+ubsan on Linux, Debug/asan+ubsan on
macOS) now run build + stats + cxxtest + test + standalone + lre, all
verified locally green in Debug and asan+ubsan configs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@syrusakbary
syrusakbary merged commit 7b8ab77 into master Jul 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.