Skip to content

Commit 1f2b4c3

Browse files
ottomatedtouzokuteemingc
authored
fix: Correctly handle shared memory for DataView initialization in deserialize_binary_form (#15028)
* Fix DataView initialization in form-utils.js:deserialize_binary_form When you call get_buffer, it returns a Uint8Array. In many JavaScript runtime environments (like Cloudflare Workers, Node.js, or modern browsers), streams often allocate chunks from a shared memory pool (slab allocation). This means the Uint8Array you get back is a view into a much larger ArrayBuffer, and it often has a non-zero byteOffset. When you pass header.buffer to DataView, it creates a view starting at the very beginning (byte 0) of the underlying memory allocation, completely ignoring the byteOffset of your header array. Because the header data usually lives at a specific offset inside that buffer, your code reads 4 bytes from the wrong location (the start of the memory slab), interprets that garbage data as a 32-bit integer, and gets 16793089 (in my test case). It then tries to read ~16MB of data, runs out of stream, and throws "data too short". You must pass the byteOffset and byteLength to the DataView constructor to ensure it looks at the correct slice of memory. I have confirmed on my code that this fix resolves my problem. * Remove unnecessary comment * add test & changeset --------- Co-authored-by: Marat Vyshegorodtsev <github@v.marat.to> Co-authored-by: Tee Ming <chewteeming01@gmail.com>
1 parent cd6837c commit 1f2b4c3

File tree

3 files changed

+35
-1
lines changed

3 files changed

+35
-1
lines changed

.changeset/thick-spiders-invite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: Correctly handle shared memory when decoding binary form data

packages/kit/src/runtime/form-utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export async function deserialize_binary_form(request) {
220220
`Could not deserialize binary form: got version ${header[0]}, expected version ${BINARY_FORM_VERSION}`
221221
);
222222
}
223-
const header_view = new DataView(header.buffer);
223+
const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength);
224224
const data_length = header_view.getUint32(1, true);
225225
const file_offsets_length = header_view.getUint16(5, true);
226226

packages/kit/src/runtime/form-utils.spec.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,33 @@ describe('binary form serializer', () => {
213213
expect(await world_slice.text()).toBe('World');
214214
expect(world_slice.type).toBe(file.type);
215215
});
216+
217+
// Regression test for https://github.com/sveltejs/kit/issues/14971
218+
test('DataView offset for shared memory', async () => {
219+
const { blob } = serialize_binary_form({ a: 1 }, {});
220+
const chunk = new Uint8Array(await blob.arrayBuffer());
221+
// Simulate a stream that has extra bytes at the start in the underlying buffer
222+
const stream = new ReadableStream({
223+
start(controller) {
224+
const offset_buffer = new Uint8Array(chunk.byteLength + 10);
225+
offset_buffer.fill(255);
226+
offset_buffer.set(chunk, 10);
227+
controller.enqueue(offset_buffer.subarray(10));
228+
}
229+
});
230+
231+
const res = await deserialize_binary_form(
232+
new Request('http://test', {
233+
method: 'POST',
234+
body: stream,
235+
// @ts-expect-error duplex required in node
236+
duplex: 'half',
237+
headers: {
238+
'Content-Type': BINARY_FORM_CONTENT_TYPE
239+
}
240+
})
241+
);
242+
243+
expect(res.data).toEqual({ a: 1 });
244+
});
216245
});

0 commit comments

Comments
 (0)