ReviewOS

zig-utils/zig-js

Private ABI: harden VM-owned identity registries

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

Parents: #493, #495 Related: #863, #867

Problem

Each private C-API VM owns two remaining process-default identity maps:

  • private_object_boxes maps script-selected Object pointers to canonical private EncodedJSValue wrappers across sibling realms.
  • private_contiguous_vectors maps VM-owned vector addresses used to validate arbitrary caller-provided snapshot pointers.

Both use std.AutoHashMapUnmanaged, so long-lived bucket placement is predictable. The object path also allocates an arena-resident Boxed wrapper before its map insertion can fail; repeated publication failure can retain unreachable wrapper storage until VM teardown.

Required design

  • Give each VM-owned registry an independent lazy secure hash context with exact pointer/integer equality and no process-default placement.
  • Publish the first context only after successful backing allocation; entropy or allocation failure leaves an empty, unseeded, retryable registry.
  • Reserve object-map storage before creating a canonical wrapper, or otherwise prove that failed publication retains no wrapper or duplicate canonical cell.
  • Preserve one canonical private object handle across sibling realms.
  • Preserve moving-GC reconciliation without allocation: remove stale keys, rekey live moved objects, and never replace an existing canonical wrapper.
  • Preserve contiguous-vector lifetime and exact validation; null, stale, forged, foreign-array, length-mismatched, and mutated snapshots remain inert.
  • Keep the existing VM/thread ownership and teardown rules.

No-workaround rules

  • No predictable seed fallback, address-derived seed, hash-only equality, object or vector cap, linear fallback, leaked wrapper/tombstone, duplicate canonical handle, disabled moving GC, copied vector to hide validation bugs, weakened pointer checks, signature/layout change, or synchronization removal.

Acceptance

  • Deterministic default-seed collision families disperse independently in both exact registries; lookup/removal/rekey remain exact.
  • Entropy and each first-publication allocation failure leave registries empty, unseeded, leak-free, and retryable.
  • Canonical object publication allocates no wrapper until insertion capacity is guaranteed, and moving-GC rekeying performs no allocation.
  • Object identity across sibling realms and contiguous-vector validation preserve all current success and rejection semantics.
  • Focused tests pass in Debug, ReleaseSafe, and TSan; moving-GC/no-GIL, threadfuzz, full units, public C, and pinned Home/Bun ABI/runtime gates pass.
9 days ago

Implemented and pushed in fix(private-abi): harden VM identity registries (#869).

The canonical object-box and contiguous-vector registries now use independent lazy secure exact-identity hash contexts. First-use entropy/allocation failure leaves each registry empty and unseeded. Object publication reserves map capacity before allocating its arena wrapper, while precise-GC reconciliation removes/rekeys in existing capacity without replacing the canonical wrapper. Vector lookup still hashes the integer pointer before any dereference and preserves exact array/vector/length/backing/content validation.

Validation on the committed source:

  • focused registry test: Debug, ReleaseSafe, and TSan; 1/1 passed with 0 leaks in each mode
  • precise moving-GC lifecycle test: 1/1 passed with 0 leaks
  • Home private ABI runtime: 392/392 linked and runtime matrix passed in Debug, ReleaseSafe, and TSan
  • threadfuzz: 400 programs from seed 1, 0 failures
  • exact clean test-parallel: 2,290 passed, 1 skipped, 0 failed, 0 leaked; 677.1 s across 10 shards
  • C API audit: 117/117; Home public: 50/50; Home private: 447/447; Bun private: 437/437
  • JSType audit: Home 97, Bun 98, shared 97, expected Bun-only 1
  • docs: 50 pages built; 328/328 links resolve

Test262 was not run because this is private native lookup-table hardening and does not change JavaScript semantics.

9 days ago

Follow-up #870 is complete in 17dea421 — fix(private-abi): harden lifecycle registry (#870). The process-global private lifecycle table now uses independent lazy secure exact-pointer hashing, failure-atomic publication, centralized exact retirement, and empty-table reset/reseed, with Debug/ReleaseSafe/TSan, external fixtures, fuzz, full units, ABI audits, and docs gates passing.

Sign in to comment on this issue.