ReviewOS

zig-utils/zig-js

Perf: bound adversarial complexity and worst-case latency

#493
Open chris opened this 8 days ago ·
8 days ago

Parent: #459 Depends on: #460, #461, #473, #475, #487

Goal

Bound adversarial algorithmic complexity and worst-case latency across parser, runtime, collections, RegExp, JSON, GC, modules, and Wasm without input blacklists or semantic shortcuts.

Scope

  • Inventory user-controlled operations with potential superlinear CPU/memory/stack behavior: tokenization/parsing, property/shape chains, hash tables, sparse arrays, proxy/accessor recursion, string search/replace, regex backtracking, JSON depth/width, module graphs, promise fanout, weak processing, GC graph traversal, structured clone, Wasm decode/validate/compile, and debugger/inspector payloads.
  • Add deterministic growth-series benchmarks and complexity assertions using exact work/limits rather than wall time alone where possible.
  • Implement owned algorithmic fixes, randomized/robust hashing, bounded stacks/worklists, cycle detection, budgets, and catchable resource failures consistent with public semantics.
  • Record tail latency and peak memory under concurrency and no-GIL schedules.

No-workaround rules

  • No known-input blacklist, reduced language/profile, arbitrary rejection below documented resource limits, catastrophic case hidden from aggregate results, or third-party parser/regex/hash implementation.

Acceptance

  • Every inventoried surface has a growth-series witness and explicit expected complexity/resource bound.
  • Adversarial cases terminate within declared budgets with exact result or specified catchable error; no crash/hang/stack overflow/leak.
  • Ordinary-case #460 performance does not regress materially.
  • Fuzz, OOM/fault, deep graph, no-GIL, TSan, and sanitizer gates cover boundary cases.
  • Security/performance docs state exact limits and evidence without claiming more than measured.

Foundation

Existing bounded Worker delivery, heap caps, parser diagnostics, regex ownership, structured clone limits, and Wasm hostile-declaration tests.

8 days ago

The collection-complexity boundary from #513 landed in c797973a. Ordinary object/Symbol keys no longer force ordered O(n) scans, one hash collision no longer disables unrelated buckets, hashes are securely seeded, and deterministic collision/OOM/bounded-growth plus moving-GC/no-GIL/TSan witnesses are checked. This closes one inventory surface, not #493: the remaining parser/runtime/RegExp/JSON/GC/module/Wasm growth-series inventory is still open.

8 days ago

#514 is complete on main and closes the strong-collection tombstone-growth complexity boundary. Stable logical cursors preserve mutation-during-iteration while physical storage compacts at a bounded threshold; focused OOM, moving-GC, no-GIL, TSan, and deterministic growth/capacity witnesses pass. Implementation/evidence chain is linked in #514.

8 days ago

#513 is complete on main and closes the strong-collection identity-key linear-scan/collision-disable complexity boundary. Exact-parent base and anti-specialization rows both use about 0.619x instructions, with identical checksums; Map remains 204/204 and Set 383/383 on both revisions. Full evidence and caveats are linked in #513 / 4adcc0d.

8 days ago

#515 is complete on main in e7d430f and closes a JSON adversarial-depth denial-of-service boundary. Deterministic subprocess coverage exercises 50,000-level arrays/objects, 20,000 mixed containers, raw JSON, reviver traversal, a valid shallow boundary, and syntax-error classification. Before: SIGSEGV/139. After: exact catchable RangeError with no JSON.parse test262 regressions.

8 days ago

Closed the temporary-copy component of JSON worst-case allocation pressure in #516. 88b5b169 avoids parser-arena allocation for every unescaped string while preserving escaped decoding and syntax rejection. A fail-on-first-allocation witness covers the property directly; exact-parent evidence measures 0.9787x / 0.9786x retired instructions across representative and anti-specialization inputs.

8 days ago

Closed the JSON reviver-source quadratic surface in #517. The 16k/32k/64k ReleaseFast growth series changes from 0.21/0.73/2.94 user-seconds to 0.05/0.05/0.10, while preserving exact parse-record mutation semantics (, moved siblings, duplicates, numeric key order). Seven-pair exact-parent evidence measures 0.1248x instructions with matching checksums and no discards.

8 days ago

Correction to the preceding summary: the implementation preserves ECMA-262 SameValue semantics, including the +0/-0 distinction, as well as moved-sibling, duplicate-key, and numeric-key ordering behavior.

8 days ago

Closed the quadratic JSON.stringify replacer-membership surface as #519. The ordered allowlist now has a per-invocation hash index, with duplicate/coercion/abrupt-completion semantics covered. Isolated membership work fell to 0.0336x instructions; exact measurement then exposed and fixed temporary bucket retention. Implementation: https://github.com/zig-utils/zig-js/commit/c823cfba and https://github.com/zig-utils/zig-js/commit/2e48c838. Evidence: https://github.com/zig-utils/zig-js/commit/11833827.

8 days ago

Closed another adversarial frontend surface in #520. Strict ordinary-function duplicate validation is now expected-linear with exact string equality; 1,024/2,048/4,096 growth rows use 0.2229x/0.1531x/0.0733x instructions versus the exact parent. No parameter limit or semantic shortcut was added. The 4,096 row is 0.126x wall time, the compared function/method corpus remains 1,275/1,275, and the concurrent scratch path is TSan-clean. Evidence: https://github.com/zig-utils/zig-js/commit/39c617e4.

8 days ago

Related lifetime/performance child #521 is complete: transient own-key hash buckets are now freed on success and every abrupt path, while result storage remains arena-owned. Exact retained RSS after ten warmups is 31.41 -> 27.36 MB at 4096 keys. Closure evidence: /zig-utils/zig-js/issues/521#issuecomment-5245487200. Named-property deletion reconstruction is deliberately separate in #522.

8 days ago

#522 is complete on main: named property deletion now uses immutable tombstones, stable slot reuse, explicit key-order history, and bounded compaction instead of rebuilding the shrinking object per deletion.

At 4,096 properties, exact-parent deletion fell from 88.625 to 0.799 ms and 528,963,775 to 11,692,360 instructions; delete/re-add fell from 88.746 to 0.846 ms and 532,216,137 to 11,897,142 instructions. The ordinary get/set control remained flat. Full evidence and verification: /zig-utils/zig-js/issues/522#issuecomment-5246336679.

8 days ago

Opened #523 for a newly isolated adversarial surface: moving GC currently clears WeakMap/WeakSet pointer indexes, causing every later full lookup pass over a wide live collection to scan linearly. The child requires relocation-stable exact identity, secure hashing, failure-atomic indexing, and 1K/2K/4K growth evidence.

8 days ago

Closed #523 with a secure, per-collection-seeded authoritative WeakMap/WeakSet identity index that survives moving GC; see 6989099b.

This removes the deterministic post-relocation linear lookup path without introducing a predictable global identity table. Exact-parent retired instructions are 0.726× / 0.565× / 0.395× at 1K / 2K / 4K mixed keys; the ordinary 4K control is 1.009×. OOM and TSan/no-GIL witnesses cover the mutation boundary. Evidence: 39eeb362.

8 days ago

Closed #524: Date setters now convert only their specification arity in fixed four-slot native storage, so hostile beyond-arity objects cannot trigger callbacks or retained arena growth. All 15 setter forms, Date 594/594, full units, no-GIL, and TSan pass. CPU is neutral and retained backing drops by 9,273,552 bytes in the exact 4K churn witness; evidence: 0e2ed155.

8 days ago

#526 closes another bounded-latency/memory failure mode: repeated Date string conversions now transfer one exact result buffer into managed storage instead of retaining proportional arena intermediates. A 4 MiB reclamation witness, allocation-zero/allocation-one failure-atomic OOM test, moving-GC witness, and no-GIL TSan pass. Exact 4K attribution removes 158,851,286 bytes of new retained backing and all 215 growth events without changing GC-cell issuance. Evidence: c7705224.

8 days ago

#528 removes quadratic CanonicalizeLocaleList duplicate scans without replacing them with attacker-predictable placement. Tables are seeded per invocation from the existing per-interpreter secure entropy stream, exact canonical bytes decide equality under collisions, and entropy failure propagates rather than falling back to clocks/thread IDs. The 4K unique row is ~0.069x wall across the exact-parent chain; duplicate-heavy 4K is 0.862x with identical frozen work.

8 days ago

Opened #530 for the adversarial deep-Shape lookup surface found while implementing #529. Computed Has/Get over N distinct names on a depth-N Shape currently walks O(N²) total chain operations. #530 requires collision-safe exact lookup, bounded retained state, no-GIL publication proof, and deterministic allocation-failure coverage.

8 days ago

#529 is closed after bounding attacker-length-dependent temporary decimal-key retention. Its exact rows deliberately make no throughput claim because the remaining O(depth) Shape lookup curve is now isolated in #530. The bounded 32 MiB witness traverses 786,432 logical indices with less than 2 MiB collected-heap delta, and deterministic OOM publication rollback passes.

8 days ago

#530 has landed collision-free persistent AVL lookup/transition indexes for attacker-controlled deep Shapes, with exact-parent work and memory evidence in 21a33f13.

At 1K/2K/4K computed property traversal, candidate instructions are 0.288x/0.190x/0.092x of the exact parent and normalized candidate instructions per property stay within 1.024x across the 4x width increase. Deterministic 4,096-name and 2,048-fanout witnesses cap tree height/comparisons at 16; OOM rollback, moving GC, bounded heap, no-GIL TSan, and collision-independent exact byte ordering are covered.

#530 remains open only for its declared 1/2/4/8 shared transition-throughput publication.

8 days ago

#536 closes another attacker-width frontend allocation surface. Unescaped JavaScript string literals now borrow exact Context-owned source slices; escaped literals materialize only at the first escape and batch-copy raw spans. The zero-byte allocator witness proves ordinary tokenization allocates nothing, while escaped decoding remains OOM-propagating and exact.

Exact-parent 1K/2K/4K growth diagnostics reduce retired instructions to 0.730x/0.718x/0.720x and median peak RSS to 0.804x/0.676x/0.665x. Full validation and commit links are recorded in #536. This closes one parser inventory item; #493 remains open for its remaining surfaces.

8 days ago

#537 closes the template-quasi allocation/growth slice. Plain untagged and tagged cooked quasis now borrow stable source or normalized backing, escaped quasis materialize only on first escape and batch-copy spans, and untagged AST construction no longer duplicates completed quasi bytes.

The 1K/2K/4K ordinary growth rows use 0.816x/0.803x/0.800x exact-parent retired instructions and remain near-linear; deterministic OOM, invalid escape, normalization, source lifetime, TSan, full units, and 84/84 affected test262 coverage pass. Full details and commit links are in #537. #493 remains open for the remaining inventoried surfaces.

8 days ago

#538 closes the escaped-IdentifierName double-scan/per-byte-copy surface. Ordinary identifiers retain the exact allocation-free source-slice loop; only the first Unicode escape enters span-based owned decoding, with exact scalar and IdentifierStart/Part validation and deterministic OOM propagation.

The 1K/2K/4K growth rows use 0.968x/0.966x/0.966x exact-parent instructions and remain near-linear, while the ordinary control improves slightly. Full validation and commit links are in #538. #493 remains open for its other inventoried surfaces.

8 days ago

Child #540 is complete in f888b717: private-name duplicate validation is now deterministic O(N log N), exact-byte, scratch-backed, and collision-independent.

Exact-parent 1K/2K/4K medians changed from 0.874/3.000/13.033 ms to 0.108/0.219/0.450 ms; whole-process retired-instruction ratios were 0.163x/0.089x/0.036x. Candidate 1K -> 4K wall growth was 4.17x versus 14.91x in the parent. The escaped 4K control was 0.044x parent wall and 0.041x instructions. Peak RSS was effectively unchanged.

The affected 2,962-case test262 class-element surface remained 2,962 pass with 0 flips/regressions; full units were 1,599 pass, 1 skip, 0 fail/leak, with parser TSan 32/32.

This also removes the complexity confounder blocking allocation child #539.

8 days ago

Child #539 is complete in 8cd6034f, following the deterministic O(N log N) validator in #540.

Plain private names are now zero-copy source slices, while escaped names remain owned, decoded, and failure-atomic. Exact-parent 1K/2K/4K instruction ratios were 0.907x/0.883x/0.870x and peak-footprint ratios were 0.868x/0.811x/0.794x; the escaped control was unchanged. The affected 2,962 test262 cases and the full 1,600-test unit inventory had 0 regressions.

8 days ago

Closed child #554: attacker-controlled statement counts no longer trigger repeated full-prefix source scans for debugger metadata. The implementation checks index-size overflow, publishes only a completely built arena-owned table, preserves every line terminator form, and leaves one/two-statement parses allocation-free. Exact-parent 1K/2K/4K instruction growth is now approximately linear; evidence is in 2de66881.

8 days ago

Adversarial nested-function complexity child #555 is complete. 00a401d7 replaces overlapping complete-source rescans with O(1)-per-reference parse-time scope tracking; 59638193 publishes the exact-parent evidence. Ordinary nesting changes from roughly fourfold to roughly twofold retired-instruction growth per depth doubling, with identical allocation counts/bytes and 2,470 → 2,470 affected test262 passes.

8 days ago

Child #556 is complete. The attacker-controlled JSON.stringify ancestor scan is now bounded to a 32-object linear prefix and O(1) expected indexed membership beyond it, without weakening sibling-alias or callback semantics. Evidence: https://github.com/zig-utils/zig-js/commit/ac9dc52b . A distinct deep-output amplification found by the same rows is tracked in #558.

8 days ago

Child #558 is complete. Runtime 3ec9847f removes recursive nested-output copying; evidence 61652299 records the exact-parent result. At JSON.stringify depth 4,096, retired instructions fell to 0.2341x and median peak RSS to 0.0311x, with Test262 remaining 66/66. Next adjacent JSON work is tracked in #557.

8 days ago

Child #557 is complete. a7da43f4 bounds ordinary JSON string output to one scan plus bulk run copies while preserving exact escape and OOM behavior; 6dbe188f publishes seven exact-parent rows. The 64K plain and sparse rows improve to 0.9565x and 0.9575x retired instructions with neutral RSS and semantic controls.

8 days ago

Closed #590 removes repeated Locale BCP-47/Unicode-keyword parsing from reflection and info consumers by publishing immutable precomputed byte ranges. The exact-parent 100-job boundary removes 499,232 property-lock acquisitions (-9.85%) while GC-cell count and collection count remain unchanged; bounded-heap, OOM, moving-GC, and TSan shared-state tests are now permanent gates.

8 days ago

#591 closed via eb661dce: forgeable NumberFormat hidden state and lazy synthesis were removed. Native sidecar branding now rejects forged receivers without observing attacker-controlled option getters; focused TSan and the full unit suite are clean.

8 days ago

#592 closed via da10d1f7. Forgeable DateTimeFormat service/locale state and the ordinary Temporal permission flag are gone; forged receivers reject without hidden-property observation. Focused TSan and full units are clean.

8 days ago

#593 closed via 724c3d73. The last generic forgeable Intl service/locale/options representation is gone; forged receivers cannot observe or derive authority from NUL-named ordinary properties. Focused TSan and full units are clean.

8 days ago

#601 includes the adversarial-complexity ruling for deferred string hashes: classification/length reject in O(1), same-length equality computes each pending hash at most once under races, and exact bytes remain authoritative. Static/interned hash consumers stay eager.

8 days ago

#601 closed via 8dd7605d with the adversarial bound intact: length/classification reject without hashing; otherwise each uninterned cell publishes deterministic XXH3 at most once under races, then exact bytes decide equality. Forced collisions, eight-thread TSan publication, and moving-GC state preservation pass.

8 days ago

Child #608 closes one linear-work duplication in Unicode identifier lexing: each first code point is now classified/decoded once, with IdentifierPart rules unchanged. The exact 1K/2K/4K instruction ratios stay flat at 0.9654x/0.9658x/0.9657x, allocations are identical, invalid UTF-8/WTF-8 and numeric-adjacency cases remain exact, and 3,230 affected test262 cases pass on both revisions.

8 days ago

Opened related bounded-parser child #609. It replaces repeated linear reserved-word list walks with one immutable length-partitioned exact table, preserving all early errors and contextual keyword rules. No runtime hash seed, mutable cache, or acceptance shortcut is in scope.

8 days ago

Child #609 closes the repeated reserved-word list-walk surface with one immutable 45-key length-partitioned exact table. Long non-keywords reject outside the keyword length range without byte comparisons; exhaustive 36/36/9 membership and uniqueness checks preserve grammar semantics. Unicode growth stays flat at 0.8241x/0.8249x/0.8258x parent instructions, and 2,325 affected test262 cases remain exact.

8 days ago

Child #611 is complete on main: benchmark contract 62a340ef, runtime fix bd24f7f2.

Functions with no direct LexicallyDeclaredNames now skip an impossible attacker-sized parameter/body conflict index. Exact-parent instructions improve to 0.8035x ASCII, 0.8462x Unicode, 0.8568x escaped, and 0.8031x nested-shadow; 11 allocation requests and 278,656 bytes disappear in the ordinary 4K rows. The direct lexical slow-path control stays exact in allocations/bytes and 1.0013x instructions. Test262 remains 2,526 -> 2,526; full units pass 1,728 plus one expected skip.

8 days ago

Opened #615 for the bounded TDZ-admission complexity slice: exact parse-plus-compile growth witnesses first, then a single exhaustive pending-name traversal with unchanged conservative TDZ semantics. This is independent of the user-owned queues/jobs work.

8 days ago

Child #618 now bounds another attacker-amplifiable compiler classifier: repeated body binding count no longer multiplies full body AST size. 3fcd7f02 freezes 1K/2K/4K and control contracts; 14ca5cd4 replaces per-binding walks with one exhaustive analysis. Exact evidence and final growth publication remain pending behind the active host-job guard.

8 days ago

Compiler child #619 is implemented benchmark-first in be27ecd4 and 8bbbf402. Deferred class bodies now scan one exact set of ordinary/non-Environment frame slots instead of rescanning the full member surface per slot, while eager computed keys and Environment-backed captures retain their existing admission semantics. Ten structural/allocation controls, focused compiler/frontend gates, and the full 1,733-pass / 1-skip unit inventory are green. Exact isolated performance and Test262 accounting remain tracked on #619 before closure.

8 days ago

Opened #621 for the remaining duplicate plain-function binding-inventory pass found after #615: shadowing and TDZ classification currently rebuild the same parameter/declaration map and traverse the same statement surface independently. The child is benchmark-first and preserves exact TDZ/opcode semantics.

8 days ago

CLI hardening child #620 is complete in 34f5c8c6. Unknown, incomplete, malformed, and trailing fixed-arity invocations now fail before corpus work; --help is side-effect-free; and the final clean valid-path smoke passed 38/38 with no surviving runner.

8 days ago

Compiler child #621 landed benchmark-first in 3bc06b01 and 84efdae5. Plain-function admission now builds one binding inventory for shadow/TDZ decisions and proves no-lexical early exits exactly. Full units, compiler TSan, and 1,869 exact Test262 cases are green; guarded performance evidence remains on #621.

8 days ago

Added #624 for the adversarial-complexity and correctness gap in plain-string replace/split. The implementation target is a bounded-linear UTF-16 scan with compact, explicitly released match-position scratch; no repeated-from-zero searches.

8 days ago

Closed the string-search complexity child #624 in e3f2ea45. Non-ASCII replace/split matching is now one KMP pass with compact u32 match positions, explicit scratch release/OOM coverage, and repeated-prefix tests; ASCII retains direct byte fast paths.

8 days ago

Child #653 is complete and closed. The bounded same-function activation cache removed the Debug detach/resize storm pathology without changing the witness or deadline; fresh Debug, ReleaseSafe, and suppression-free TSan no-GIL shard 3/4 all passed. Closure evidence: /zig-utils/zig-js/issues/653#issuecomment-5360869417

8 days ago

#662 replaced per-transition-map entropy acquisition with root-secret, domain-separated keyed derivation and retained collision-resistant SipHash lookup. Its exact-parent cold evidence shows 0.795x lifecycle wall time with exact checksums/finalizers and bounded RSS. The ordinary warmed shape/property control remains pending before #662 closes.

8 days ago

#664 adds an exact UTF-16 boundary for JSON.stringify indentation. The current ten-byte slice can split canonical WTF-8 inside an astral scalar; the child requires bounded prefix scanning, canonical lone-surrogate materialization, and focused JSON/Test262/full-unit evidence.

8 days ago

Child #664 is complete on main via 0052b2aa: the JSON.stringify gap respects the first ten UTF-16 code units without invalid byte slicing. ASCII stays allocation-free, flat Latin-1 conversion is bounded, and split astral input preserves the exact lone high surrogate. Full units and the nonzero 66-case Test262 JSON.stringify subtree pass without regressions.

8 days ago

Opened #665 to remove backing-byte String exotic descriptor indexing. The current path can misreport non-ASCII lengths and slice invalid multibyte backing; the child requires exact UTF-16 units, canonical surrogate handling, bounded representation-aware access, and focused/full regression gates.

8 days ago

Child #665 is complete on main via f30b9e0b: backing-byte String exotic indexing was replaced by bounded representation-aware UTF-16 access. This removes invalid multibyte slices, keeps flat Latin-1 direct access O(1), and makes spread/rest linear rather than repeated full transcoding. Full units and five nonzero Test262 subtrees pass with zero regressions.

8 days ago

Opened #666: flat Latin-1 String.prototype.toString/valueOf can feed physical one-byte storage into the canonical WTF-8 StringCell constructor, while valid calls allocate unnecessarily. The fix returns the existing primitive/StringData Value directly and keeps the non-generic realm TypeError contract exact.

8 days ago

Completed child #666 via 4c922933 and fa4d29cd: String value extraction no longer reinterprets representation-specific bytes or allocates a replacement cell. The allocation-failure witness covers ASCII, Latin-1, BMP, astral, and lone-surrogate Values under canonical and physical flat storage. Test262 14/14; full units 1,756 passed, 1 skipped, 0 failed/leaked. #666 is closed.

8 days ago

Opened #667 for a correctness/bounded-cost gap in String relational ordering. The current raw-byte iterator can collapse two flat Latin-1 code units into one UTF-8 scalar; the child replaces it with the existing streaming representation-aware iterator and covers all four relational operators without allocation.

8 days ago

Child #667 is complete via 75e56136: all four String relational operators use one bounded streaming UTF-16 iterator, eliminating the raw-byte ambiguity without transcoding or allocation. Physical flat-Latin-1 coverage passed in tree-walker and VM; Test262 184/184 and full units 1,758 passed, 1 skipped, 0 failed/leaked.

8 days ago

Opened #668 to keep dynamic parsing bounded and representation-safe. Physical flat Latin-1 source must be converted once at lexer ingress rather than misread as UTF-8; canonical/ASCII input remains borrowed and conversion OOM remains observable.

8 days ago

Child #668 is complete via 355329dc: all dynamic lexer ingresses are representation-safe and bounded. A fail-first flat-source conversion returns exact OutOfMemory before lexing with zero successful allocations; canonical input stays borrowed. Test262 620/620; full units clean.

8 days ago

Opened #669 for the representation-safe, bounded Object.prototype.toString output path. The fix preserves observable tag lookup while using checked exact sizing and one final allocation across canonical and physical flat StringData.

8 days ago

Child #670 now owns a versioned exact algorithmic-growth evidence profile for classifier fixes whose optimized boundary falls below stable wall/cycle/energy resolution while the exact parent grows superlinearly. It explicitly preserves the existing full-efficiency 5% policy and forbids threshold relaxation, asymmetric work, extrapolation, or throughput claims from diagnostic rows. The concrete blocker and two rejected #615 reference attempts are recorded in #670 and the #615 audit.

8 days ago

Child #669 is complete in 170aa4a8: custom Object.prototype.toString tags now compose into one exact final representation-aware StringCell backing, including managed-GC accounting and flat-Latin-1 canonical hashing. Test262 is 41/41 with zero flips; full units are 1,764 pass + 1 skip with zero failures/leaks; focused managed-GC TSan is clean.

8 days ago

#670 additive algorithmic-growth evidence contract landed in fabf6c7c. Exact-parent inputs remain complete and their ordinary summaries are reconstructed; the new profile scores only retired instructions at or below 5% RSD and exact allocation replay across at least three widths. Wall/cycle/energy/RSS/thermal values remain diagnostic and cannot support performance or efficiency claims.

8 days ago

Related exact-growth preparation landed through #671 in da430db1: structured-clone flat-Latin-1 encoding counts canonical bytes, reserves the complete field once, and fills it directly. This removes temporary conversion allocation from that future storage path without changing the wire contract.

8 days ago

Opened #675 to close four latent representation-dependent JSON paths before flat Latin-1 activation. Scope is bounded canonical egress for replacer keys, gap, and raw payloads with exact ordering/OOM behavior and a real storage-switch witness; no retry, alternate parser/stringifier, or user-owned scheduling surface.

8 days ago

Closed latent representation-dependent JSON correctness child #675 with fix(json): canonicalize StringData readers (#675). The physical flat Latin-1 witness now proves non-ASCII replacer, gap, and raw JSON paths retain canonical semantics without global canonicalization or a second pass. Full units: 1768 pass, 1 skip, 0 fail, 0 leak; affected Test262 subtrees had 0 flips.

8 days ago

#621's bounded-growth evidence landed in 1a0cf6e2. The frozen binding-inventory family stays linear from 1K through 4K on both sides (first-to-last exponent 1.003), while the candidate removes the duplicate traversal and reduces retired instructions to about 0.94x at every width with exact allocation replay. #493 remains open for its wider adversarial-complexity scope.

8 days ago

26b8c248 removes the sloppy mapped-arguments VM admission barrier under #729. Test262 arguments-object remains 263/263 (mapped 43/43), with full normal and TSan unit gates clean; direct eval, non-simple parameters, legacy caller reflection, and native entry remain explicit.

8 days ago

Related bounded compiler child #730 now covers adversarially nested destructuring declarations without delegating the pattern to the tree walker: binding inventory, TDZ classification, and native iterator/object-rest lowering are activation-owned. No performance claim is being made; correctness/full-corpus gates remain in progress.

8 days ago

Adversarial iterator behavior was tightened via #750 in 28482211: Math.sumPrecise performs one observable next lookup and preserves abrupt/IteratorClose error precedence. Exact subtree evidence: 10 pass -> 10 pass; 0 flips.

8 days ago

The adversarial-input bound for Math.sumPrecise landed via #751 in 25b8d23e: production now enforces the exact 2^53 - 1 limit and retains RangeError over abrupt iterator close. Test262: 10 pass -> 10 pass; 0 flips.

8 days ago

Completed linked JSON hardening slice #755 in a06685ff: reviver source-record object indexes now use one lazy, unpredictable per-parse hash context with exact-byte equality and failure-atomic installation. JSON units are 15/15, focused TSan is clean, and exact-parent test/built-ins/JSON remains 165 pass / 0 fail. This closes one attacker-controlled native hash-table surface without a deterministic fallback or semantic limit.

8 days ago

Completed another attacker-controlled native hash-table slice in #757: 36ad04d4 gives JSON.stringify replacer PropertyList deduplication one lazy secure per-invocation context with exact-byte equality. The focused 4 KiB/NUL/wrapper/duplicate witness and TSan paths pass, JSON units are 16/16, and exact-parent JSON Test262 remains 165 pass / 0 fail. No property cap, fallback seed, or reduced semantics was introduced.

8 days ago

Completed Proxy trap-result hash hardening in #758: 9906d22f replaces default-seeded ownKeys duplicate/invariant membership with one lazy secure exact-byte context. String/Symbol duplicates, escaped NUL, 4 KiB keys, empty results, and non-configurable/non-extensible errors are covered; focused TSan is clean and exact-parent Proxy ownKeys Test262 remains 27 pass / 0 fail. No caps, fallback seeds, or reduced Proxy semantics were added.

8 days ago

Completed transient enumeration hash hardening in #759: 1adc7b7b securely seeds for-in, C API property snapshots, and host/ordinary own-key membership. The common ordinary no-host path now skips the table entirely, and contains+put became one hashed insertion. Tree/required-VM, moving-GC, host overlap, NUL, and 4 KiB witnesses pass; 155 exact-parent Test262 cases across for-in/Reflect/Proxy ownKeys remain 155 pass / 0 fail.

8 days ago

Opened parser hardening child #761 for the remaining source-controlled declaration/early-error maps. Its contract is one lazy parse-root secure context, inherited by nested template parses and fed by the existing interpreter secure stream in production; it explicitly forbids per-table entropy calls, predictable fallbacks, caps, or weakened early errors.

8 days ago

Completed Annex B declaration-name hardening via #760 in 52a7c387: sloppy legacy candidate membership now has one lazy unpredictable exact-byte context per scan, with the node-specific eligibility set and lexical NameStack unchanged. Focused normal/TSan coverage is green and exact-parent Annex B Test262 remains 845 pass / 0 fail with zero flips.

8 days ago

Parser declaration/name membership hardening is complete in #761 via fix(parser): seed declaration name membership (#761): every parser source-string table now shares one lazy realm-derived secure context, with exact-parent Test262 at 23,647 pass / 1 known failure on both sides (0 regressions).

8 days ago

Completed the remaining C/private ABI transient property-name hardening in #762 via fix(c-api): seed property name membership (#762). Necessary host/prototype membership is now lazy and securely seeded; an already-unique own-key snapshot no longer pays for redundant deduplication. Exact-parent relevant Test262 remains 155/155.

8 days ago

#763 is complete in fix(private-abi): seed borrowed string interning (#763): private borrowed-view and heap-snapshot attacker-controlled string maps now use independent lazy secure exact-byte contexts. Normal/TSan 7/7; exact-parent relevant Test262 flips 0.

8 days ago

String interning placement hardening is complete in #764 via 288b4893: deterministic semantic content hashes are no longer used for intern shard or bucket placement. Concurrent initialization, entropy failure, and all first-insert allocation failures are covered in normal and TSan; relevant Test262 flips 0.

8 days ago

Compiler identifier hash-flood hardening child #770 is complete: runtime a3c39817 and exact-parent growth evidence 3d249965. Default-bucket doubling is reduced from ~3.81x/~3.91x to ~2.02x/~2.01x instructions with exact checksums and unchanged allocation request counts.

8 days ago

Adversarial-latency child #781 landed in a42cf61e: "".repeat(Number.MAX_SAFE_INTEGER) no longer executes 4,294,967,295 no-op iterations. Empty input is O(1) after exact count validation, and non-empty results are bounded before allocation.

8 days ago

Bounded string construction progress: #782 landed on main in 872dbbb5. Two-value concatenation now checks exact canonical size before allocation, uses one owned output buffer, and retains the existing non-empty StringData cell for empty concatenation. Focused normal/TSan, exact-parent Test262, and threadfuzz 400/0 are green; the full unit gate remains pending.

8 days ago

Closed attacker-controlled runtime PrivateEnvironment hash placement through #848 in 08e2c7ba. Each evaluated class now owns one realm-derived secure exact-byte context; nested shadow clones preserve it, inner maps rehash flattened outer entries, and candidate-free classes reuse their outer map without entropy/allocation. Exact-parent ordinary and required-VM class inventories are each 8,426/8,426 on both sides with zero flips/divergences; full units and threadfuzz are clean.

8 days ago

Completed the runtime module-graph hash-table slice in #849 via fix(modules): seed runtime graph indexes (#849). Persistent registries, loader sources, active caches, dependency maps, and export maps are now lazily keyed from the realm Shape secret with failure-atomic publication and exact equality. Exact-parent module Test262 inventories stayed flat (599 + 941 passes, 0 failures/flips), required-VM parity had 0 divergences, full units passed 2,249 with 1 platform skip, and threadfuzz passed 400/400.

8 days ago

Runtime declarative binding-table hardening landed in fix(runtime): seed environment binding maps (#850). One lazy realm-secret exact-byte context now covers all six Environment value/tag/alias maps, with first-writer, OOM, no-GIL/TSan, moving-GC, Test262, required-VM, full-unit, and threadfuzz evidence recorded in #850. This completes the scoped runtime Environment collision surface; #493 remains open for its broader complexity inventory.

8 days ago

Closed child #851 in a1816a9d: Object-owned accessor, attribute, private-brand, C-host descriptor-cell, creation-order, and named-compaction indexes now use one unpredictable exact-byte context per Object. Default-seed collision families disperse under the keyed context; shape-less objects fail closed through secure entropy; first publication is OOM-atomic. Exact-parent Object/Reflect/Proxy/class Test262 stayed 12,301/12,301 with zero VM divergence.

8 days ago

Landed the remaining default-context runtime string-map hardening in #852 via ce1225a5. Interpreter.symbols now uses lazy root-Shape-derived keyed hashing with failure-atomic publication. Full units, TSan/ReleaseSafe probes, 400-program threadfuzz, 3,973 exact-parent Test262 cases, VM parity, and ABI gates are clean.

8 days ago

Landed Array hole-index hardening in #853 via 0a5d1f70. Script-selected dense indices no longer enter a fixed-seed hash set: each cold sidecar has one Shape-derived exact-integer context with failure-atomic publication. Full units, ReleaseSafe, direct no-GIL TSan, 400-program threadfuzz, and 3,133 exact-parent Test262/VM cases are clean.

8 days ago

Landed bounded Wasm exception-root deduplication in #854 via 44c72b15. Arbitrary JS externref bits no longer enter a fixed-seed hash set; native exception publication now uses allocation-free worst-case O(n log n) sort-and-unique over exact bits. OOM, nested roots, TSan/no-GIL, full units, threadfuzz, and byte-identical 18,778-pass packed Wasm inventories are recorded on the child.

8 days ago

Completed child #855 in 8afcf79: all four attacker-populable parser cover-grammar identity indexes now use the parser's lazy keyed context with exact identity and failure-atomic first publication. Deterministic legacy-collision, OOM replay, Debug/TSan, full-unit, 400-program threadfuzz, exact-parent Test262 (0 flips), and ABI evidence are recorded on #855.

8 days ago

Completed compiler child #856 in f16ce8a: the remaining three attacker-populable AST-identity plans now share the compilation's lazy keyed context with exact identity and failure-atomic publication. Debug/TSan, full units, 400-program threadfuzz, 10,423 exact-parent Test262 cases (0 flips), OOM replay, and ABI evidence are recorded on #856.

8 days ago

Completed the interpreter-local AST/RegExp identity-map child in #857 via fix(interpreter): seed AST identity caches (#857). This removes the remaining default-seeded pointer maps for Annex B eligible nodes, string literal sites, tagged-template sites, and RegExp matcher state. Exact-parent Test262 was 1560/1560 with byte-identical diagnostics; full units, focused TSan, threadfuzz, and ABI gates are green.

8 days ago

Completed JSON active-cycle hash-placement hardening in #858 via fix(json): seed active-cycle identity index (#858). This preserves #556’s relocation-stable O(n) active-path design while removing its remaining process-default identity placement. Full units, focused TSan, threadfuzz, ABI gates, and byte-identical 66/66 JSON.stringify Test262 are green.

8 days ago

Child #859 is complete in fix(structured-clone): secure object memoization (#859). Structured-clone graph memoization no longer exposes deterministic raw-pointer placement: it uses a lazy secure keyed context over exact relocation-stable identities, with direct legacy-collision dispersion and failed-first-insert retry witnesses. Full units, ReleaseSafe/TSan structured-clone tests, threadfuzz 400/0, public C API, and Home private ABI gates are green.

8 days ago

Child #860 is complete in fix(debugger): seed statement location registry (#860). The persistent AST-pointer registry now uses a lazy realm-secret exact index; direct legacy-collision dispersion and first-publication OOM retry are covered. Exact-parent statements remain 9,337/9,337 with 0 flips, full units and threadfuzz are clean.

8 days ago

Closed #861 in fix(reflect): bound integer own-key merging (#861). Script-chosen canonical indices no longer enter fixed-seed transient hash tables during [[OwnPropertyKeys]]; the three paths now share deterministic worst-case-O(n log n) numeric sort/dedup. Exact-parent own-key/Object/JSON Test262 stayed 183/183 passing, full units were 2,276 pass / 1 skip / 0 fail / 0 leak, threadfuzz was 400/0, and public/private ABI gates were green through TSan.

8 days ago

Closed #862 in fix(atomics): seed shared waiter domains (#862). The process-wide typed-array/Wasm waiter index now uses lazy process-secret placement under its existing mutex, with exact storage-plus-offset equality and failure-atomic publication. Exact-parent Atomics Test262 stayed 390/390 passing, no-GIL typed-array wait passed 1/1, full units were 2,277 pass / 1 skip / 0 fail / 0 leak, and threadfuzz was 400/0.

8 days ago

Completed another adversarial-complexity boundary in #863 via fix(private-abi): harden heap snapshot identity (#863).

Heap-snapshot/reachability raw cell identities and persistent arena stable IDs now use lazy secure per-map hashing with failure-atomic context publication. Dense root indices use O(1) flags instead of hashing. Exact snapshot/reachability tests pass in Debug, ReleaseSafe, and TSan; full units pass 2,280 with 1 skip and no failures/leaks; public C and pinned Home/Bun ABI/runtime gates pass. Test262 is N/A for this host-only ABI change.

8 days ago

Completed the aggregate-exception graph hardening follow-up in #864 via fix(wasm): seed exception collection identity (#864).

This removes predictable pointer hashing from Wasm GC exception traversal with a lazy secure exact identity set, failure-atomic publication, iterative cycle handling, and no shared exception epoch. #864 contains the exact Debug/ReleaseSafe/TSan, Wasm inventory, threadfuzz, full-unit, C API, and pinned ABI evidence.

8 days ago

Completed the private Wasm streaming capability hardening in #865 via fix(wasm): harden streaming compiler handles (#865).

The context-free pinned addBytes feed no longer uses predictable sequential authority tokens or default integer hashing. Handles and registry placement use separate secure entropy, exact live collisions resample, and entropy/allocation failure publishes no partial state. #865 records exact Debug/ReleaseSafe/TSan, pinned ABI, Wasm inventory, threadfuzz, docs, and clean full-suite evidence.

8 days ago

Follow-up hardening landed via #866 in c6642bb2: serialized-script owner handles are now secure capabilities with separately seeded exact lookup, collision resampling, failure-atomic publication, and exact single-winner release.

8 days ago

Completed the process-global script-context hash-table slice in #867 via fix(private-abi): seed script context registry (#867). Home-controlled 32-bit identities now use lazy independently seeded exact placement with failure-atomic first publication and transfer; exact full units, TSan, threadfuzz, and pinned ABI/runtime gates are green.

8 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.

8 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.

8 days ago

Security/latency follow-up #871 is complete in 516a8d59: context and worker inspector refs now use independently randomized capabilities in one securely keyed exact-token registry. Forged, stale, wrong-kind, and wrong-thread inputs are rejected before dereference; adversarial legacy-bucket collisions are covered.

8 days ago

Build-gate follow-up #872 is complete in 99e534be: all four public C/C++ hosts now link and pass under TSan, closing the sanitizer coverage gap exposed while hardening inspector capabilities.

8 days ago

Related hardening landed: #873 securely seeds the exact-thread-ID ThreadLocal value index, preserves lifecycle/GC semantics, and is implemented by 3cf4429e.

8 days ago

#926 now has a measured growth curve rather than a code-inspection suspicion, so it is a confirmed instance of this issue's class.

Parser.checkPrivateNameUses copies every inherited private name into a fresh map per nested class. Against frozen workloads at 256/512/1024/2048, retained parse-arena bytes grow x3.89 per doubling for N sibling inner classes and x3.85 for an N-deep chain, against a x2.01 source. Both controls stay linear, so the cost is the inherited copy and not the nesting: at N=2048 the growth shape runs 194x slower and retains 31x more memory than a control that parses the same number of nested classes with nothing to copy — 297 MB of retained arena for a 98 KB source.

The source is linear and attacker-controlled in shape, which is the property this issue cares about. Allocation counts were identical across iterations and across three independent runs. Workloads and the semantic baseline landed in 411b901 ahead of any parser change; measurement detail is in #926.

One correction worth carrying here: the deep shape was drafted as a control on the reasoning that a chain copies a growing set once per level. That is quadratic too (1+2+...+N), so it is a second growth shape, not a control — it now measures at 0.479 of the siblings constant, as N^2/2 vs N^2 predicts.

8 days ago

#926 is closed, fixed and measured in e07b13b — the first of this issue's quadratics to be taken all the way from suspicion to a landed fix with a before/after curve.

Parser.checkPrivateNameUses no longer copies the inherited private-name set per nested class; it extends the enclosing set in place and unwinds it. At width 2048 that is 154x faster and 25x less retained memory (232.6 ms -> 1.51 ms, 297.0 MB -> 11.6 MB), and the speedup grows with N — 51x at 1024, 154x at 2048 — because the exponent changed rather than the constant. Growth per doubling is now x2.08 against a x2.01 source. Both controls sit at or below their baseline, which is what confirms the win is the removed copy and not removed work. test262 across the four affected subtrees: 0 fail, every count matching baseline.

The generalizable part for the rest of this issue's class: the copy was replaceable by an undo log only because the structure is asked exactly one question (contains, over a void value). Worth checking that property first on the other candidates — where a scope map carries a payload, the same trick does not port. #927 is precisely that case.

Two more instances of the same copy-per-nested-scope shape are now filed, both verified by reading the code and both deliberately carrying no performance claim until measured:

  • #927 — runtime private-environment flattening in interpreter.zig. The serious site absorbs a full copy of the enclosing environment into every class's published map on the long-lived arena, so it is retained rather than transient, and it is re-paid per class evaluation. It bounds how much the #926 parse-time fix is worth end to end.
  • #928 — checkLexicalDupes rebuilds the whole subtree var-name set at every nested block, arena-retained, O(N x D).

Method note worth carrying forward from #926: retained bytes are a cleaner algorithmic signal than wall time. Once the arena passes a few hundred MB the time ratio picks up a memory-system tax and overshoots the true exponent (x5.3 where N^2 predicts x4.0), while the byte ratio converged on x3.89. Allocation counts were bit-identical across three independent runs; timings on sub-millisecond controls spread up to 3x on three samples and needed 15.

8 days ago

#928 is closed and fixed in 0fb72d2, the second of this issue's quadratics taken from suspicion to a measured fix after #926.

checkLexicalDupes no longer re-collects the whole subtree's var names at every nested scope. At width 2048: 199x faster and 46x less retained memory (166.7 ms -> 0.84 ms, 291.5 MB -> 6.4 MB). As with #926 the speedup climbs with N (19x -> 42x -> 88x -> 199x), and time now grows x2.00 per doubling. 11,890 test262 tests across 15 subtrees: 0 fail under both the old and new parser.

Refinement to the note I left here after #926. I wrote that the undo-log trick works "only because the structure is asked exactly one question" and does not port where a scope map carries a payload. #928 is a counterexample to the second half: its map does carry a payload (a depth), and it still worked. What actually mattered was that the question is answerable from state that is stack-disciplined, so a scope can restore exactly what it changed. The payload only meant the undo log must restore a previous value rather than delete a key. That seems the better test for the remaining candidates, including #927.

What both fixes had in common, worth reusing: freeze the workloads first, and include a control that separates the two plausible causes. For #928 that was wide: N sibling blocks run the scan exactly as often as the growth shape does, yet stayed flat, which showed the cost was each scan's depth and not the number of scans. Removing the lexical names would not have shown that, because it turns the scan off altogether.

Adversarially reviewing #928 also turned up 22 pre-existing early-error bugs, each confirmed by execution against node and against the pre-fix parser: #929 (2 valid programs rejected) and #930 (20 invalid programs accepted). One #930 family, function bodies the walk never reaches, would widen the walk. That is exactly the change that could bring #928's quadratic back, so anyone fixing it should re-run the frozen #928 rows.

8 days ago

Two more instances from this stretch, one fixed and one newly measured:

  • #931 fixed (33e5222) — a correctness fix, but relevant here because the obvious implementation would have added a quadratic: a parse-time var check per catch clause re-walks the whole catch block, and the parser has no nesting limit. It was placed in the #928 walk instead, which checks each var once.
  • #932 filed — nested destructuring catch parameters are quadratic inside function bodies (x3.9 per doubling, 1.87 s at depth 4000), while the identical nesting is linear at top level and linear with plain parameters. Root cause not yet located.

A caveat worth carrying for the whole class: the #926 and #928 frozen workloads parse top-level scripts, so they are blind to anything confined to function bodies. #932 is one such effect, and plain nested blocks inside a function also show steepening growth (x2.44 then x4.05). The #926/#928 results stand for the shapes they measured, but function-body variants of those workloads should be added before treating this class as closed.

8 days ago

Closed #1029 in 18a6fb2: nested template substitutions now evaluate through an explicit action stack instead of native recursion. The depth-300 regression passes normally and under TSan; the current-main full unit gate passed 2,567/2,568 with one skip, and the retained 20,000-link flat-chain path showed no measured regression.

Sign in to comment on this issue.