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
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
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.jsreported 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_objpublication was unsynchronized. A peer could therefore copy a torn rare-state record while constructing a typed-array view frommemory.buffer.Implementation
Commit
0ad2eadaserializes 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
memory.bufferidentity, detachment/growth semantics, bounds, shared-memory aliasing, and imported/exported wrapper identity.memory.bufferreads versus repeatedmemory.grow()and typed-array view churn with deterministic checks.cve/mc-life-sab-refchurn.jsunder no-GIL TSan.Focused evidence
git diff --checkclean