mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
LibJS: Refresh TypedArray cached data pointers on shared memory grow
WebAssembly.Memory({shared:true}).grow() reallocates the underlying
AK::ByteBuffer outline (kmalloc+kfree) but, per the threads proposal,
must not detach the associated SharedArrayBuffer.
ArrayBuffer::detach_buffer was the only path that walked m_cached_views
and cleared the cached raw m_data pointer on each TypedArrayBase, so
every existing view retained a dangling pointer into the freed outline.
The AsmInterpreter GetByValue / PutByValue fast paths dereference that
cached pointer directly, yielding a use-after-free triggerable from
JavaScript.
Add ArrayBuffer::refresh_cached_typed_array_view_data_pointers() which
re-derives m_data for each registered view from the current outline
base (and refreshes UnownedFixedLengthByteBuffer::size), and call it
from Memory::refresh_the_memory_buffer on the SAB-fixed-length path
where detach is spec-forbidden.
This commit is contained in:
Notes:
github-actions[bot]
2026-04-20 07:44:15 +00:00
Author: https://github.com/Yayoi-cs 🔰 Commit: https://github.com/LadybirdBrowser/ladybird/commit/d8aee7f1e6d
@@ -0,0 +1,43 @@
|
||||
<!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: 2, shared: true });
|
||||
const oldView = new Uint8Array(mem.buffer);
|
||||
oldView[0x0] = 0xde;
|
||||
oldView[0x1] = 0xad;
|
||||
oldView[0x2] = 0xbe;
|
||||
oldView[0x3] = 0xef;
|
||||
|
||||
mem.grow(1);
|
||||
|
||||
const newView = new Uint8Array(mem.buffer);
|
||||
// verify array created from the new memory.buffer after grow() reads the same underlying memory before the grow.
|
||||
println(`newView[0x0]: 0x${newView[0x0].toString(16)}`);
|
||||
println(`newView[0x1]: 0x${newView[0x1].toString(16)}`);
|
||||
println(`newView[0x2]: 0x${newView[0x2].toString(16)}`);
|
||||
println(`newView[0x3]: 0x${newView[0x3].toString(16)}`);
|
||||
|
||||
// verify array created from the new memory.buffer before grow() reads the same underlying memory after the grow.
|
||||
newView[0x4] = 0xab;
|
||||
newView[0x5] = 0xad;
|
||||
newView[0x6] = 0x1d;
|
||||
newView[0x7] = 0xea;
|
||||
println(`oldView[0x4]: 0x${oldView[0x4].toString(16)}`);
|
||||
println(`oldView[0x5]: 0x${oldView[0x5].toString(16)}`);
|
||||
println(`oldView[0x6]: 0x${oldView[0x6].toString(16)}`);
|
||||
println(`oldView[0x7]: 0x${oldView[0x7].toString(16)}`);
|
||||
|
||||
// verify array created from the new memory.buffer before grow() writes the same underlying memory after the grow.
|
||||
oldView[0x8] = 0xca;
|
||||
oldView[0x9] = 0xfe;
|
||||
oldView[0xa] = 0xba;
|
||||
oldView[0xb] = 0xbe;
|
||||
println(`newView[0x8]: 0x${newView[0x8].toString(16)}`);
|
||||
println(`newView[0x9]: 0x${newView[0x9].toString(16)}`);
|
||||
println(`newView[0xa]: 0x${newView[0xa].toString(16)}`);
|
||||
println(`newView[0xb]: 0x${newView[0xb].toString(16)}`);
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user