Compare commits

...

2 Commits

Author SHA1 Message Date
autofix-ci[bot]
ad2dc9bcd9 [autofix.ci] apply automated fixes 2026-01-17 12:09:26 +00:00
Claude Bot
868fe0e020 fix(uv): add bounds check for buffer count in readv/writev to prevent panic
Replace unsafe `@intCast(bufs.len)` with `std.math.cast` to safely convert
the buffer count from `usize` to `c_uint` before passing to libuv functions.
Returns EINVAL error instead of panicking when the buffer count exceeds the
maximum value of c_uint.

Fixes #26187

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 12:07:38 +00:00
3 changed files with 83 additions and 4 deletions

View File

@@ -219,10 +219,16 @@ pub const Async = struct {
const bufs = args.buffers.buffers.items;
const pos: i64 = args.position orelse -1;
const nbufs = std.math.cast(c_uint, bufs.len) orelse {
task.result = .{ .err = .{ .errno = @intFromEnum(bun.sys.E.INVAL), .syscall = .read } };
task.globalObject.bunVM().eventLoop().enqueueTask(jsc.Task.init(task));
return task.promise.value();
};
var sum: u64 = 0;
for (bufs) |b| sum += b.slice().len;
const rc = uv.uv_fs_read(loop, &task.req, fd, bufs.ptr, @intCast(bufs.len), pos, &uv_callback);
const rc = uv.uv_fs_read(loop, &task.req, fd, bufs.ptr, nbufs, pos, &uv_callback);
bun.debugAssert(rc == .zero);
log("uv readv({d}, {*}, {d}, {d}, {d} total bytes) = scheduled", .{ fd, bufs.ptr, bufs.len, pos, sum });
},
@@ -237,12 +243,18 @@ pub const Async = struct {
return task.promise.value();
}
const nbufs = std.math.cast(c_uint, bufs.len) orelse {
task.result = .{ .err = .{ .errno = @intFromEnum(bun.sys.E.INVAL), .syscall = .write } };
task.globalObject.bunVM().eventLoop().enqueueTask(jsc.Task.init(task));
return task.promise.value();
};
const pos: i64 = args_.position orelse -1;
var sum: u64 = 0;
for (bufs) |b| sum += b.slice().len;
const rc = uv.uv_fs_write(loop, &task.req, fd, bufs.ptr, @intCast(bufs.len), pos, &uv_callback);
const rc = uv.uv_fs_write(loop, &task.req, fd, bufs.ptr, nbufs, pos, &uv_callback);
bun.debugAssert(rc == .zero);
log("uv writev({d}, {*}, {d}, {d}, {d} total bytes) = scheduled", .{ fd, bufs.ptr, bufs.len, pos, sum });
},

View File

@@ -293,12 +293,15 @@ pub fn preadv(fd: FileDescriptor, bufs: []const bun.PlatformIOVec, position: i64
var req: uv.fs_t = uv.fs_t.uninitialized;
defer req.deinit();
const nbufs = std.math.cast(c_uint, bufs.len) orelse return .{
.err = .{ .errno = @intFromEnum(std.posix.E.INVAL), .fd = fd, .syscall = .read },
};
const rc = uv.uv_fs_read(
uv.Loop.get(),
&req,
uv_fd,
bufs.ptr,
@intCast(bufs.len),
nbufs,
position,
null,
);
@@ -327,12 +330,15 @@ pub fn pwritev(fd: FileDescriptor, bufs: []const bun.PlatformIOVecConst, positio
var req: uv.fs_t = uv.fs_t.uninitialized;
defer req.deinit();
const nbufs = std.math.cast(c_uint, bufs.len) orelse return .{
.err = .{ .errno = @intFromEnum(std.posix.E.INVAL), .fd = fd, .syscall = .write },
};
const rc = uv.uv_fs_write(
uv.Loop.get(),
&req,
uv_fd,
bufs.ptr,
@intCast(bufs.len),
nbufs,
position,
null,
);
@@ -382,6 +388,8 @@ pub inline fn write(fd: FileDescriptor, buf: []const u8) Maybe(usize) {
pub const Tag = @import("./sys.zig").Tag;
const std = @import("std");
const bun = @import("bun");
const Environment = bun.Environment;
const FileDescriptor = bun.FileDescriptor;

View File

@@ -0,0 +1,59 @@
// https://github.com/oven-sh/bun/issues/26187
// Panic "integer does not fit in destination type" when reading files on Windows x86_64
// The fix adds bounds checking on buffer counts before passing to libuv functions
import { expect, test } from "bun:test";
import { closeSync, openSync, readFileSync, readvSync, writeFileSync, writevSync } from "fs";
import { tempDir } from "harness";
test("readFileSync should not panic", () => {
using dir = tempDir("issue-26187", {
"test.txt": "Hello, World!",
});
const content = readFileSync(`${dir}/test.txt`, "utf8");
expect(content).toBe("Hello, World!");
});
test("writeFileSync should not panic", () => {
using dir = tempDir("issue-26187", {});
writeFileSync(`${dir}/output.txt`, "Test content");
const content = readFileSync(`${dir}/output.txt`, "utf8");
expect(content).toBe("Test content");
});
test("fs.readvSync with multiple buffers should not panic", () => {
using dir = tempDir("issue-26187", {
"multi.txt": "AAAAABBBBB",
});
const fd = openSync(`${dir}/multi.txt`, "r");
try {
const buf1 = Buffer.alloc(5);
const buf2 = Buffer.alloc(5);
const bytesRead = readvSync(fd, [buf1, buf2], 0);
expect(bytesRead).toBe(10);
expect(buf1.toString()).toBe("AAAAA");
expect(buf2.toString()).toBe("BBBBB");
} finally {
closeSync(fd);
}
});
test("fs.writevSync with multiple buffers should not panic", () => {
using dir = tempDir("issue-26187", {});
const fd = openSync(`${dir}/writev.txt`, "w");
try {
const buf1 = Buffer.from("Hello");
const buf2 = Buffer.from("World");
const bytesWritten = writevSync(fd, [buf1, buf2]);
expect(bytesWritten).toBe(10);
} finally {
closeSync(fd);
}
const content = readFileSync(`${dir}/writev.txt`, "utf8");
expect(content).toBe("HelloWorld");
});