ReviewOS

zig-utils/zig-js

Wasm Threads: synchronize memory wrapper growth publication

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

CI root: https://github.com/zig-utils/zig-js/actions/runs/29691148416/job/88204023748 Replacement CI: https://github.com/zig-utils/zig-js/actions/runs/29696338163 Parent: #287 Related: #265, #285, #286 Roadmap: #142, #134

Root cause

The no-GIL TSan corpus case cve/mc-life-sab-refchurn.js reported a data race between:

  • wasm.api.memoryBufferGetter copying a Memory wrapper's rare-state record; and
  • wasm.exec.memoryGrowAddressed → wasm.api.memoryDidGrow replacing the current buffer wrapper after shared-memory growth.

The backing store was shared correctly, but the JavaScript wrapper's buffer_obj publication was unsynchronized. A peer could therefore copy a torn rare-state record while constructing a typed-array view from memory.buffer.

Implementation

Commit 0ad2eada serializes each Memory wrapper independently through its existing no-GIL object backing lock. Readers take a complete record snapshot; growth publishes the replacement while holding the same lock. The old raw-pointer accessor was removed so new live readers cannot bypass the synchronization boundary.

Unshared detachment and replacement now share the same wrapper critical section. Shared historical buffers remain fixed-length aliases of the same Shared Data Block.

Acceptance

  • Make memory-wrapper backing publication guarded for no-GIL shared realms without serializing unrelated memories.
  • Preserve memory.buffer identity, detachment/growth semantics, bounds, shared-memory aliasing, and imported/exported wrapper identity.
  • Cover concurrent memory.buffer reads versus repeated memory.grow() and typed-array view churn with deterministic checks.
  • Pass focused normal and suppression-free TSan tests.
  • Re-run cve/mc-life-sab-refchurn.js under no-GIL TSan.
  • Confirm the replacement Wasm Threads and full CI gates are green.

Focused evidence

  • normal regression: 3/3 passed
  • TSan regression: 3/3 passed
  • exact no-GIL TSan corpus witness: 1/1 passed in 25.2s
  • source diff: git diff --check clean
9 days ago

Landed 0ad2eada on main. Focused regression: normal 3/3 and suppression-free TSan 3/3. Exact CI witness zig build threads-test -Dthreads-parallel-js=true -Dthreads-case=cve/mc-life-sab-refchurn.js -Dtsan=true: 1/1 passed in 25.2s. Replacement CI: https://github.com/zig-utils/zig-js/actions/runs/29696338163

9 days ago

Replacement run 29697559754 is active with all four normal unit shards green. The decisive no-GIL TSan corpus shards, including shard 1 containing the prior Memory-growth race, are still running.

9 days ago

Completed by 7650986. CI run https://github.com/zig-utils/zig-js/actions/runs/29697559754 finished green across all 40 jobs, including no-GIL TSan corpus shards 0-3 and the original shard-1 race witness. The few emitted warnings were explicitly tolerated non-race corpus timeout/failure outcomes; no ThreadSanitizer failure remained.

9 days ago

Follow-up landed in 235f6124. The wrapper snapshot lock from this issue made each memory.buffer read internally consistent, but an unshared grow could still detach that returned buffer before the same expression constructed its TypedArray. The ReleaseSafe PR-249 witness reproduced this as TypeError: Cannot construct a TypedArray on a detached buffer at iteration 16.

Unshared relocation now opens a realm stop acknowledged only at complete statement/Wasm/native boundaries, arbitrated against shared-heap GC publication through the existing conductor. Shared Memory remains on the historical non-detaching SAB path; the no-GIL baseline was not weakened or exempted.

Evidence: 30/30 repeated ReleaseSafe processes plus final exact 1/1; focused normal and TSan 1/1; exact suppression-free TSan corpus witness 1/1 in 24.03s; full units 1,479/1,479 with zero skips, failures, or leaks.

Sign in to comment on this issue.