ReviewOS

zig-utils/zig-js

Compiler: seed binding and capture membership maps

#770
Closed chris opened this 8 days ago ·
8 days ago

Parents: #493, #496 Related: #461, #616, #618, #619, #761

Problem

Parser declaration membership is securely seeded by #761, but bytecode admission and lowering rebuild attacker-controlled identifier sets in deterministic std.StringHashMapUnmanaged tables. Frame-slot names, lexical scopes, mapped-parameter deduplication, shadow/TDZ inventories, loop-head captures, repeated-body captures, and deferred-class frame-name membership therefore remain targetable with collision sets computed against Zig's process-default string hash. A source that passes the parser can still force pathological compiler lookup/insertion cost.

Scope

  • Freeze production parse + admission/compile growth rows for ordinary and adversarial identifier families before changing the compiler.
  • Give one compilation-owned secure hash context to every identifier-content map used by program, plain-function, generator, and async admission/lowering.
  • Share that context across nested compiler/prepass structures within the same compilation; do not request entropy per map or per function.
  • Keep zero/one-name common paths allocation- and entropy-minimal where existing specialized representations already do so.
  • Preserve exact identifier bytes, scope/shadowing/TDZ decisions, slot numbering, capture classification, unsupported-reason identity, structural checksums, and OOM rollback.
  • Make entropy failure explicit and catchable before publishing a partially compiled Chunk.

No-workaround rules

  • No predictable fallback seed, source rewriting, identifier blacklist, heuristic width cutoff, skipped compiler pass, forced tree-walker fallback presented as hardening, process-global mutable seed, or per-table entropy calls.
  • No stacks, queues, jobs-runtime, Promise, microtask, Worker-lifecycle, or Map/Set iterator changes.

Acceptance

  • Benchmark-first ordinary/adversarial rows freeze equal source work, admission identity, structural checksum, allocation replay, and 1K/2K/4K growth.
  • Every compiler string-membership table uses the one compilation-owned secure context; no deterministic default content map remains on a source-controlled identifier path.
  • Tests prove distinct seeds preserve semantics/layout, known fixed-seed collision families cannot target production placement, and entropy/OOM failure leaves no published partial Chunk.
  • Plain/generator/async/program compilation, nested functions, lexical shadowing, TDZ, mapped parameters, loop captures, and deferred classes preserve exact behavior.
  • Focused compiler/frontend, full units, relevant Test262, OOM, and sanitizer gates pass with exact before -> after accounting.
  • Exact-parent growth evidence and methodology are checked in; parent/sibling issues are cross-linked without claiming noisy wall time.
8 days ago

Benchmark-first boundary landed in 2f2c8a55 before any compiler change. The production frontend runner now has paired 1K/2K/4K ordinary and deterministic-default-bucket identifier rows with identical numeric suffixes, prefix lengths, source-construction work, fresh arenas, plain-function admission, structural Chunk checksums, and untimed allocation replay support. The adversarial names share fourteen low Wyhash(seed=0) bits, matching Zig StringHashMap's hash & capacity_mask initial placement through the scored capacities.

One-job pre-fix diagnostics (not publication evidence): ordinary 0.40 / 0.84 / 1.84 ms, targeted bucket 4.13 / 15.42 / 62.86 ms. Doubling width therefore produces roughly 4x targeted elapsed while the paired control remains near 2x. Frozen one-job checksums are ordinary 342155663 / 886624823 / 177082273 and targeted 367374999 / 388727068 / 242482827. frontend-parse-benchmark-bin passes; flips 0 Test262 cases. Ten-job counter/allocation replay is pending a competitor-free host window.

8 days ago

Compiler hardening landed in a3c39817. One lazy compile-owned Wyhash context now covers frame slots, lexical scopes, mapped-parameter deduplication, shadow/TDZ inventories, loop and repeated-body capture sets, deferred-class membership, and nested program/plain/generator/async compilation. Zero/one-name specialized paths still request neither entropy nor allocation; entropy/allocation failure publishes neither a context nor a partial API result. Fixed-seed layout, known default-collision dispersion, shared-lazy-state, and exhaustive allocation-failure tests pass.\n\nExact ten-job diagnostics at 1K/2K/4K identifiers: default-bucket instructions 700,834,106 / 2,672,935,644 / 10,436,230,512 → 69,527,264 / 141,211,361 / 282,582,150. All checksums match. Allocation requests remain 32,090 / 62,980 / 124,570 for both paired shapes; the secure state adds a constant 240 bytes over ten compilations. Validation: compiler units 91/91, compiler TSan 91/91, frontend 26/26, full units 1,939 passed + 1 skipped with zero failures/leaks, and Test262 statements plus function-code 9,554/9,554 with zero flips.\n\nThe exact-parent artifact is waiting for the repository competitor guard to clear an unrelated Home ts_checker build; no noisy wall-time claim will be published.

8 days ago

Exact-parent evidence is complete in 3d249965. Six schema-v2 artifacts cover ordinary and deterministic-default-bucket inventories at 1K/2K/4K, each with two order-balanced pairs, direct logical-revision binary hashes, matching checksums, exact frontend allocation replay, native instruction/cycle/energy counters, and per-invocation competitor rejection. Stable instruction growth for the adversarial family changes from 3.81x / 3.91x on doubling to 2.02x / 2.01x, matching the ordinary control; wall time remains diagnostic only. All acceptance items are now satisfied.

8 days ago

Follow-up #856 is complete in f16ce8a: #770's compilation-owned seed now also protects the three AST-identity plans that were intentionally outside its identifier-content-map scope. The compiler now has no default AST-pointer AutoHashMap remaining.

8 days ago

Follow-up #857 is complete in fec38b3b. It preserves #770’s secure exact-name membership and additionally keys the separate eligible-declaration AST-node map from the interpreter’s lazy secure identity context. Annex B exact-parent Test262 remains 1086/1086 with byte-identical diagnostics.

Sign in to comment on this issue.