diff --git a/src/bun.js/webcore/blob/read_file.zig b/src/bun.js/webcore/blob/read_file.zig index db36497de4a..52705c7f3c1 100644 --- a/src/bun.js/webcore/blob/read_file.zig +++ b/src/bun.js/webcore/blob/read_file.zig @@ -384,8 +384,9 @@ pub const ReadFile = struct { // add an extra 16 bytes to the buffer to avoid having to resize it for trailing extra data if (!this.could_block or (this.size > 0 and this.size != Blob.max_size)) - this.buffer = std.ArrayListUnmanaged(u8).initCapacity(bun.default_allocator, this.size + 16) catch |err| { + this.buffer = std.ArrayListUnmanaged(u8).initCapacity(bun.default_allocator, @as(usize, this.size) + 16) catch |err| { this.errno = err; + this.system_error = bun.sys.Error.fromCode(bun.sys.E.NOMEM, .read).toSystemError(); this.onFinish(); return; }; diff --git a/test/js/web/fetch/blob-oom.test.ts b/test/js/web/fetch/blob-oom.test.ts index dc7dd16bc09..e9d40914ae6 100644 --- a/test/js/web/fetch/blob-oom.test.ts +++ b/test/js/web/fetch/blob-oom.test.ts @@ -1,7 +1,7 @@ import { setSyntheticAllocationLimitForTesting } from "bun:internal-for-testing"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test"; import { unlinkSync } from "fs"; -import { tempDirWithFiles } from "harness"; +import { isWindows, tempDirWithFiles } from "harness"; import path from "path"; describe("Memory", () => { beforeAll(() => { @@ -142,3 +142,19 @@ describe("Bun.file", () => { expect(async () => await Bun.file(tmpFile).arrayBuffer()).not.toThrow(); }); }); + +describe.if(!isWindows)("Bun.file on character devices", () => { + test("slice() with a very large max length throws OOM without crashing", async () => { + // Regression test: reading a character device (e.g. /dev/null) with a + // max_length close to u52 max used to overflow `this.size + 16` in + // ReadFile.runAsyncWithFD and panic in debug builds. Also verifies that + // OOM from the buffer allocation is propagated to JS rather than + // silently returning an empty result. + expect( + async () => + await Bun.file("/dev/null") + .slice(0, 2 ** 52 - 15) + .bytes(), + ).toThrow(); + }); +});