Skip to content
File

Blob: src/workerd/api/tests/response-uaf-test.js

javascript45 lines
1// Copyright (c) 2026 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5// Regression test for a use-after-free: creating a Response from an ArrayBuffer,
6// then transferring the buffer via structuredClone and collecting the clone,
7// must not cause the Response body read to access freed memory.
8//
9// Without the fix, this crashes under ASAN with:
10// heap-use-after-free READ of size 1024
11 
12export default {
13 async test() {
14 // Fixed-size ArrayBuffer (non-resizable). The UAF is not specific to
15 // resizable buffers — it affects any ArrayBuffer whose backing store can
16 // be transferred away.
17 const buffer = new ArrayBuffer(1024);
18 new Uint8Array(buffer).fill(0x42);
19 
20 const res = new Response(buffer);
21 
22 // Transfer detaches the original buffer. The clone becomes the sole
23 // JS-visible owner of the backing store.
24 structuredClone(buffer, { transfer: [buffer] });
25 
26 // Collect the clone (which was not assigned to a variable). This frees
27 // the backing store memory if nothing else prevents it.
28 gc();
29 
30 // Reading the body must not touch freed memory.
31 const result = new Uint8Array(await res.arrayBuffer());
32 if (result.length !== 1024) {
33 throw new Error(`Expected 1024 bytes, got ${result.length}`);
34 }
35 // Verify the data is intact (not garbage from freed memory).
36 for (let i = 0; i < result.length; i++) {
37 if (result[i] !== 0x42) {
38 throw new Error(
39 `Byte ${i}: expected 0x42, got 0x${result[i].toString(16)}`
40 );
41 }
42 }
43 },
44};