Files
ladybird/Tests/LibWeb/Text/input/WebAssembly-Memory-grow-shared-stale-view.html
Yayoi-cs 0b9636fadf LibJS: Only cache TypedArray data pointers for owned buffers
WebAssembly.Memory-backed ArrayBuffers wrap external
ByteBuffer storage. When that memory grows,
ByteBuffer::try_resize() may realloc the backing storage while
old fixed-length buffer objects remain reachable from JS.

TypedArrayBase cached m_data for all fixed-length buffers, and
the asm interpreter fast path dereferenced that cached pointer
directly. For wasm memory views this could leave a stale
pointer behind across grow().

Restrict cached typed-array data pointers to fixed-length
ArrayBuffers that own stable ByteBuffer storage.
External/unowned buffers, including WebAssembly.Memory
buffers, now keep m_data == nullptr and fall back to code that
re-derives buffer().data() on each access.

Add regressions for both the original shared-memory grow case
and the second-grow stale-view case.
2026-04-25 06:11:18 +02:00

47 lines
2.0 KiB
HTML

<!doctype html>
<script src="include.js"></script>
<script>
test(() => {
// regression test for the stale-view case on shared WebAssembly.Memory.
// A typed array created from the old memory.buffer before grow() should still read and write the same underlying memory after the grow.
const mem = new WebAssembly.Memory({ initial: 1, maximum: 1000, shared: true });
const firstView = new Uint8Array(mem.buffer);
mem.grow(1);
const secondView = new Uint8Array(mem.buffer);
mem.grow(100);
const thirdView = new Uint8Array(mem.buffer);
// Writes through the newest view must be visible through the oldest view
thirdView[0x0] = 0x11;
thirdView[0x1] = 0x22;
thirdView[0x2] = 0x33;
thirdView[0x3] = 0x44;
println(`firstView[0x0]: 0x${firstView[0x0].toString(16)}`);
println(`firstView[0x1]: 0x${firstView[0x1].toString(16)}`);
println(`firstView[0x2]: 0x${firstView[0x2].toString(16)}`);
println(`firstView[0x3]: 0x${firstView[0x3].toString(16)}`);
// Writes through the oldest view must be visible through the newest view.
firstView[0x4] = 0x55;
firstView[0x5] = 0x66;
firstView[0x6] = 0x77;
firstView[0x7] = 0x88;
println(`thirdView[0x4]: 0x${thirdView[0x4].toString(16)}`);
println(`thirdView[0x5]: 0x${thirdView[0x5].toString(16)}`);
println(`thirdView[0x6]: 0x${thirdView[0x6].toString(16)}`);
println(`thirdView[0x7]: 0x${thirdView[0x7].toString(16)}`);
// And via the intermediate view.
secondView[0x8] = 0x99;
secondView[0x9] = 0xaa;
secondView[0xa] = 0xbb;
secondView[0xb] = 0xcc;
println(`firstView[0x8]: 0x${firstView[0x8].toString(16)}`);
println(`firstView[0x9]: 0x${firstView[0x9].toString(16)}`);
println(`firstView[0xa]: 0x${firstView[0xa].toString(16)}`);
println(`firstView[0xb]: 0x${firstView[0xb].toString(16)}`);
});
</script>