Skip to content

fix(js): stop application JavaScript from aborting core through URL.parse, stack traces and import() - #341

Merged
oglassdev merged 2 commits into
mainfrom
fix/js-abort-paths
Oct 13, 2026
Merged

oglassdev merged 2 commits into
mainfrom
fix/js-abort-paths

Conversation

@oglassdev

@oglassdev oglassdev commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Refs #300. Application JavaScript could make native code panic inside a V8 callback, which aborts the whole process hosting core. This closes the two paths the issue reported and a third found while auditing, all inside chunk-js, with no fork and no dependency change. One further abort remains open and is described below.

Closed

Path Cause Now
URL.parse reentrancy Upstream runs a subclass constructor and a brand setter between the parse op and reading its result, so a nested parse consumed the result and the op panicked. URL.parse is our own: it checks with the captured URL.canParse and constructs through the captured URL. It returns a plain URL even when called on a subclass, as the spec says.
Call-site cache poisoning Call sites handed to Error.prepareStackTrace cached values with an index write that an inherited Array.prototype setter could swallow, then unwrapped them. Error.prepareStackTrace is ignored and stack is always a string, so call-site objects never reach application code. Thrown-error text is unchanged.
Failed dynamic import A failed import() whose rejection had a non-string message and no stack was unwrapped unchecked. import() always rejects with TypeError: Runtime imports are unavailable. Only import(import.meta.url) resolved before.

The README documents the three surface changes. The one most likely to be noticed is Error.prepareStackTrace: libraries that expect call-site arrays from .stack no longer get them.

Still open

  • errorAdditionalPropertyKeys (abort). deno_core sizes a Vec from an application-supplied array when converting a thrown error, so throw Object.assign(new Error("x"), { [Symbol.for("errorAdditionalPropertyKeys")]: new Array(4294967295) }) aborts the process, from an invocation, a top-level throw, a microtask error or an unhandled rejection. Review showed this can be closed inside chunk-js, but it is a larger change than this PR: take over the exception and promise-rejection boundaries (the report-exception and unhandled-rejection hooks, plus a raw TryCatch around invocation and module evaluation) and convert with a bounded conversion instead of from_v8_exception. It is tracked on chunk-js: URL.parse reentrancy and stack call-site caching can abort the process #300.
  • A getter that loops during error conversion (hang, not abort). Object.defineProperty(e, "message", { get() { for (;;); } }); throw e outlives the deadline, because the watchdog terminates once and conversion calls the getter again. It predates this PR and is outside this class of bug.

Checks

  • Each of the six aborts in the issue reproduced as SIGABRT before the fix.
  • cargo test -p chunk-js (27 passed), cargo clippy -p chunk-js --all-targets -- -D warnings, cargo fmt -p chunk-js --check, and oxfmt --check on web.js and the README.
  • About 260 single-process probes over the reachable surface; the harness is not committed.

🤖 Generated with Claude Code

Application JavaScript could panic native code inside a V8 callback, which aborts the process hosting core:

- URL.parse ran a subclass constructor or a brand setter between parsing and reading the serialization, so a nested parse left the op without one. It now constructs through the captured URL constructor.
- Error.prepareStackTrace received call sites whose cached source positions an Array.prototype setter could poison. Stacks are now always formatted by deno_core, and the hook is ignored.
- A failed dynamic import unwrapped the rejection's message. import() now always rejects.

Refs #300.
@oglassdev
oglassdev merged commit 5441be5 into main Oct 13, 2026
13 checks passed
@oglassdev
oglassdev deleted the fix/js-abort-paths branch October 13, 2026 00:06
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.

1 participant