ReviewOS

zig-utils/zig-js

Private ABI: harden heap-snapshot identity traversal

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

Parent: #493 Related: #763, #859

Problem

The pinned private heap-snapshot/profile boundary walks an attacker-shaped JavaScript heap with ReachabilityVisitor.seen, a fixed-seed AutoHashMapUnmanaged(usize, usize) keyed by raw cell address. Arena-backed VMs also retain address-to-stable-ID mappings in CContextGroup.heap_snapshot_ids, another fixed-seed usize map. The same visitor powers the public ZJSValueIsReachable extension.

A program controls heap width, graph shape, allocation history, and repeated snapshot timing. Predictable pointer hashing can therefore amplify traversal and stable-ID assignment before the host receives any output. #763 secured the snapshot's string interning but did not cover cell identity.

ReachabilityVisitor.roots_seen is a third hash table, but its keys are dense one-based node indices allocated by the same visitor; hashing is unnecessary. The private diagnostic inspector's active-path map is excluded because its recursion depth is already hard-capped at eight.

Required design

  • Add one lazy, exact usize identity-map primitive for private C/ABI data, seeded from secure host entropy with no predictable fallback.
  • Publish the context only after the first backing allocation succeeds; failed entropy/allocation leaves context, count, and capacity empty and retryable.
  • Use that primitive for transient reachability cell indexing and persistent arena-cell snapshot IDs. Precise-GC cells must retain their existing relocation-stable odd-ID path and never enter the address table.
  • Replace root-index hash membership with dense per-node flags and preserve first-root ordering exactly.
  • Keep graph traversal, weak/ephemeron policy, edge ordering, stable IDs, retained-size dominators, output schemas, and C/private ABI ownership exact.
  • Do not cap heap width, omit nodes/edges, expose partial output, or substitute linear scans.

Acceptance

  • A deterministic legacy usize collision family disperses under the new context with exact get/remove behavior.
  • Entropy failure and first-allocation failure publish no context/table; retry succeeds exactly.
  • Root dedup uses dense flags with exact first-root order and OOM cleanup.
  • Repeated arena snapshots retain stable even IDs; precise cells retain relocation-stable odd IDs.
  • Snapshot V8/GCDebugging/profile outputs, reachability, dominators, and allocation-failure tests pass in Debug, ReleaseSafe, and TSan.
  • Full sharded units, public C API, Home/Bun private ABI audits and runtime boundaries pass; Test262 is recorded N/A because this is host-only ABI.
  • Commit and closure evidence link exact commands/results.
9 days ago

Implemented and pushed to main as fix(private-abi): harden heap snapshot identity (#863).

The change replaces both raw-address AutoHashMapUnmanaged boundaries with a lazy exact usize map keyed by secure host entropy. Failed entropy or first allocation publishes neither context nor table and remains retryable. Dense one-based root indices now use parallel flags, preserving first-root order while eliminating unnecessary hashing. Precise-GC cells retain relocation-stable odd IDs; arena cells retain stable even IDs. Traversal and root insertions roll back on OOM, and failed visitors cannot expose a partial graph.

Validation on the exact landed source:

  • zig build test -Dtest-filter='private heap snapshot': 6/6 Debug; 6/6 ReleaseSafe; 6/6 TSan, with 0 failures/leaks/races.
  • zig build test -Dtest-filter='private reachability roots': 1/1 Debug; 1/1 ReleaseSafe; 1/1 TSan.
  • zig build test -Dtest-filter='context groups share values': 1/1 ReleaseSafe and 1/1 TSan; the same test is included in the clean Debug full suite.
  • zig build test-parallel: 2,280 passed, 1 skipped, 0 failed, 0 leaked across 10 shards.
  • zig build c-api-audit home-public-abi-audit home-private-abi-audit bun-private-abi-audit private-jstype-abi-audit: public C 117/117, Home public 50/50, Home private 447/447, Bun private 437/437; all layout checks passed.
  • zig build test-c-api test-home-private-abi test-private-heap-snapshot: public C hosts passed; Home private runtime linked/passed 392/392; both pinned Home/Bun heap-snapshot ownership/runtime fixtures passed.
  • zig build test-private-heap-snapshot -Doptimize=ReleaseSafe and -Dtsan=true: both pinned consumer fixtures passed in both configurations.

Test262: N/A. This changes only host-side C/private-ABI diagnostic data structures and does not alter ECMAScript semantics or the corpus denominator.

9 days ago

Follow-up #869 is complete in fix(private-abi): harden VM identity registries (#869). The remaining VM-owned canonical-object and contiguous-vector identity registries now use independent lazy secure exact-identity maps with failure-atomic first publication and allocation-free moving-GC rekeying. Full validation evidence is recorded in #869.

Sign in to comment on this issue.