ReviewOS

zig-utils/zig-js

Runtime: reserve evaluation completion roots before bounded scripts

#645
Closed chris opened this 9 days ago ·
9 days ago

Problem

Fresh exact Zig dev.1818 Linux TSan CI reaches past #634 and executes the complete bounded recovery script successfully, then loses its completion value at the host boundary: TSan unit shard 1 fails in Interpreter.pushTempRoot while growing gc_temp_roots from Context.evaluateWithThis.

That ordering lets attacker-controlled JavaScript exhaust the realm, catch every script-visible OOM, and produce a valid result, only for the embedding call to return a host error.OutOfMemory because precise-root storage was allocated afterward. The result also needs to be read back from its root slot after a moving tail collection, rather than from a potentially stale native local.

Required implementation

  • Reserve one evaluation-completion root before timer checkpoints or script execution can consume the remaining bounded budget.
  • Keep the placeholder in the active interpreter root set so nested temporary roots stack above it and cannot consume the reservation.
  • Publish a successful completion into that slot without allocation.
  • Return the possibly relocated value from the root slot after all microtask, Thread keepalive, and requested-GC checkpoints.
  • Restore the exact prior root depth on every success/error path.
  • Do not raise the heap limit, bypass accounting, skip tail checkpoints, swallow OOM, or rely on conservative native-stack retention.

Acceptance

  • The unchanged 4 MiB torn-object recovery witness passes on exact dev.1818 Linux and under TSan.
  • A focused allocation-failure unit proves completion publication succeeds after its allocator is exhausted.
  • Moving-GC and parallel Thread completion-root witnesses remain green.
  • Full normal/TSan unit shards report zero leaks and no new sanitizer findings.

Parent: #628 Roadmap parent: #480 Related: #634, #636

9 days ago

Implemented in ee476876.

evaluateWithThis now pushes an undefined placeholder into the active interpreter's precise root stack before timer checkpoints or script execution. Nested roots stack above it. A successful outcome replaces that slot without allocation, tail microtask/Thread/GC work traces it, and the host returns the possibly relocated slot value before restoring the prior depth.

Exact dev.1818 focused evidence:

  • allocation-exhaustion reservation unit: normal + TSan pass;
  • unchanged 4 MiB torn-object recovery witness: normal + TSan pass;
  • parallel thrown-Thread completion-root witness: normal + TSan pass;
  • 3/3 in each mode, 0 leaks, 0 sanitizer reports;
  • Test262 flips: 0 (host-boundary root ownership only).

Keeping open for fresh Linux shard confirmation.

9 days ago

Fresh Linux run 32388315489 passed the normal recovery witness, but TSan unit shard 2 exhausted every one of the 500 post-recovery attempts (made == 0) under competing shard load. There was no torn publication and no leak, but this does not satisfy the unchanged acceptance oracle, so I am keeping the issue open. The exact unchanged TSan witness passes locally 3/3 at 12.5–13.9 s on current main. Fresh Linux validation for c6dcbe6d is pending; the 4 MiB cap and success requirements remain unchanged.

9 days ago

Acceptance is complete. Completion-root reservation in ee476876 remains covered by focused allocation-exhaustion, moving-GC, and parallel Thread root tests. The exact 4 MiB publication oracle now passes fresh Linux normal shard 3 and TSan shard 3, with zero leaks; #657 removed only a false conservative-stack reclamation assumption.

Sign in to comment on this issue.