You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Immutable ArrayBuffer's are almost here. Are we ready? #66346
Every API that receives an ArrayBuffer needs to be updated to correctly handle immutable ArrayBuffers. It looks like we still have a fair number that do not handle them correctly. We need a comprehensive audit and update.
Hi @jasnell, I was a little bit curious regarding the serialization part of this audit.
I reproduced on 26.4 with --js-immutable-arraybuffer and read main at ce9d650. v8.serialize() / v8.deserialize() and the advanced IPC codec keep a bare immutable ArrayBuffer immutable, but any view over one comes back mutable. structuredClone, MessageChannel and Worker keep it. #66350 already lists this. As far as I can tell the host-object record written by _writeHostObject and node_ipc_serdes.cc carries nothing about the backing store.
What I have in mind is to keep the documented DefaultSerializer contract where only the referenced range is stored and let V8 carry that range as a buffer for immutable views only.
uint32 typeIndex | 0x80000000 // high bit means an ArrayBuffer value follows instead of length + raw bytes
value view.buffer.sliceToImmutable(byteOffset, byteOffset + byteLength) // writeValue(). V8 tags it immutable and rebuilds it immutable
The invariants are the exact range, Buffers staying Buffers and the encoding of mutable views remaining byte-for-byte unchanged. I verified them in a userland prototype on 26.4. The ipc_serdes reader can follow with ReadValue(). The writer needs a way to build an immutable copy of a range from C++. V8's public API does not offer it yet. So node:v8 first and IPC as a follow-up.
On compatibility, Node 25.0.0 accepts today's encoding of an immutable view as a mutable view and rejects the new record. Would this record shape be acceptable for immutable views, including rejection by older readers? Or would you prefer a different compatibility strategy?
Details and measurements
In the prototype on 26.4, 15/15 mutable values are byte-identical to v8.serialize(). A 6-byte view of a 36-byte immutable buffer serializes to 16 bytes with none of the surrounding bytes. Today it is 11 bytes and mutable on read. Mixed messages, repeated references and cycles round-trip. Zeroing the transport buffer after reading leaves the immutable view intact.
I could also test two more cases that come with the change. A mutable view read from an immutable transport buffer comes out mutable instead of aliasing the transport. An unknown type index throws an explicit error instead of a TypeError on undefined.
With Node 25.0.0 reading messages written by 26.4, a bare immutable ArrayBuffer fails with Unable to deserialize cloned data. A view in the current encoding is read and writable. The new record fails with ReadRawBytes() failed. The closest precedents are the BigInt64Array #43571 and Float16Array #55996 index additions. Those added new types rather than re-encoding a view older readers already accept.
Activity
panva commentedon Sep 27, 2026
Some issues to look at and chip down on in #66350. Most likely not exhaustive.
RohanMali2003 commentedon Oct 8, 2026
I can work on adding support/error handling for immutable ArrayBuffers in node:crypto (randomFill, randomFillSync, getRandomValues)
Hakaou commentedon Oct 21, 2026
Hi @jasnell, I was a little bit curious regarding the serialization part of this audit.
I reproduced on 26.4 with
--js-immutable-arraybufferand readmainat ce9d650.v8.serialize()/v8.deserialize()and theadvancedIPC codec keep a bare immutableArrayBufferimmutable, but any view over one comes back mutable.structuredClone,MessageChannelandWorkerkeep it. #66350 already lists this. As far as I can tell the host-object record written by_writeHostObjectandnode_ipc_serdes.cccarries nothing about the backing store.What I have in mind is to keep the documented
DefaultSerializercontract where only the referenced range is stored and let V8 carry that range as a buffer for immutable views only.The invariants are the exact range, Buffers staying Buffers and the encoding of mutable views remaining byte-for-byte unchanged. I verified them in a userland prototype on 26.4. The
ipc_serdesreader can follow withReadValue(). The writer needs a way to build an immutable copy of a range from C++. V8's public API does not offer it yet. Sonode:v8first and IPC as a follow-up.On compatibility, Node 25.0.0 accepts today's encoding of an immutable view as a mutable view and rejects the new record. Would this record shape be acceptable for immutable views, including rejection by older readers? Or would you prefer a different compatibility strategy?
Details and measurements
In the prototype on 26.4, 15/15 mutable values are byte-identical to
v8.serialize(). A 6-byte view of a 36-byte immutable buffer serializes to 16 bytes with none of the surrounding bytes. Today it is 11 bytes and mutable on read. Mixed messages, repeated references and cycles round-trip. Zeroing the transport buffer after reading leaves the immutable view intact.I could also test two more cases that come with the change. A mutable view read from an immutable transport buffer comes out mutable instead of aliasing the transport. An unknown type index throws an explicit error instead of a
TypeErroronundefined.With Node 25.0.0 reading messages written by 26.4, a bare immutable
ArrayBufferfails withUnable to deserialize cloned data. A view in the current encoding is read and writable. The new record fails withReadRawBytes() failed. The closest precedents are the BigInt64Array #43571 and Float16Array #55996 index additions. Those added new types rather than re-encoding a view older readers already accept.