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.memoryBufferGettercopying a Memory wrapper's rare-state record; andwasm.exec.memoryGrowAddressed→wasm.api.memoryDidGrowreplacing 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.bufferidentity, detachment/growth semantics, bounds, shared-memory aliasing, and imported/exported wrapper identity. - Cover concurrent
memory.bufferreads versus repeatedmemory.grow()and typed-array view churn with deterministic checks. - Pass focused normal and suppression-free TSan tests.
- Re-run
cve/mc-life-sab-refchurn.jsunder 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 --checkclean