Skip to content

Fix crash on cyclic re-export of an imported binding#1583

Merged
saghul merged 1 commit into
quickjs-ng:masterfrom
littledivy:fix/cyclic-reexport-crash
Jul 17, 2026
Merged

Fix crash on cyclic re-export of an imported binding#1583
saghul merged 1 commit into
quickjs-ng:masterfrom
littledivy:fix/cyclic-reexport-crash

Conversation

@littledivy

@littledivy littledivy commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Re-exporting an imported binding (import { x } from "m"; export { x }) from a module in an import cycle could dereference a NULL JSVarRef and crash (SIGSEGV). While a peer in the cycle is still unlinked its import slot is NULL, and both js_build_module_ns() and js_inner_module_linking() used that slot directly.

Minimal reproducer (segfaults on master):

// p.mjs
import { q } from "./r.mjs";
export { q as p };
// r.mjs
import { p } from "./p.mjs";
export { p as q };
// entry: import { p } from "./p.mjs";

js_get_local_export_var_ref() resolves such a binding by following the import alias to its source module, reusing js_resolve_export1's JSResolveState visited-set to detect re-export cycles. When the binding is genuinely unresolvable (a pure re-export cycle with no concrete binding) it throws a SyntaxError instead of crashing.

case before after
valid cyclic re-export (#567) stopgap SyntaxError evaluates correctly
pure re-export cycle, no binding crash SyntaxError
mutual re-export of imports crash resolved or SyntaxError

This also lets the test_cyclic_import.js case that carried a FIXME(bnoordhuis) work as intended. Fixes #567.

Disclaimer: This is an AI-assisted patch from the v8x project.

@littledivy
littledivy force-pushed the fix/cyclic-reexport-crash branch 3 times, most recently from f59b4bc to c711c78 Compare July 17, 2026 15:02
@@ -1,10 +1,3 @@
/*---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're here, can you please rename the test to bugXXX.js ?

@littledivy littledivy Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to bug567.js

@littledivy
littledivy force-pushed the fix/cyclic-reexport-crash branch from c711c78 to ddc13c5 Compare July 17, 2026 15:11
Re-exporting an imported binding (`import { x } from "m"; export { x }`)
from a module that participates in an import cycle could dereference a NULL
JSVarRef and crash. While a peer in the cycle is still unlinked its import
slot is NULL, and both js_build_module_ns() and js_inner_module_linking()
used that slot directly.

Resolve such a binding by following the import alias to its source module
(js_get_local_export_var_ref). When the binding is genuinely unresolvable,
e.g. a pure re-export cycle with no concrete binding, throw a SyntaxError
instead of crashing.

This makes a valid cyclic re-export evaluate correctly, so the
test_cyclic_import.js case that previously threw a stopgap SyntaxError now
passes. Fixes quickjs-ng#567.
@littledivy
littledivy force-pushed the fix/cyclic-reexport-crash branch from ddc13c5 to 51f6e8e Compare July 17, 2026 15:44
@saghul
saghul merged commit ef7a3a7 into quickjs-ng:master Jul 17, 2026
128 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.

Segmentation fault with cyclic imports

2 participants