Add 0-length buffer checks to os.read & os.write
This helps prevent errors related to undefined pointers being passed through to some OS apis when slices have 0 length. Tests have also been added to catch these cases.
This commit is contained in:
committed by
Andrew Kelley
parent
7d0d99aac0
commit
b5222f86ee
@@ -660,6 +660,7 @@ pub const ReadError = error{
|
||||
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
|
||||
/// The corresponding POSIX limit is `math.maxInt(isize)`.
|
||||
pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
|
||||
if (buf.len == 0) return 0;
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.ReadFile(fd, buf, null, std.io.default_mode);
|
||||
}
|
||||
@@ -787,6 +788,7 @@ pub const PReadError = ReadError || error{Unseekable};
|
||||
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
|
||||
/// The corresponding POSIX limit is `math.maxInt(isize)`.
|
||||
pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
|
||||
if (buf.len == 0) return 0;
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.ReadFile(fd, buf, offset, std.io.default_mode);
|
||||
}
|
||||
@@ -1045,6 +1047,7 @@ pub const WriteError = error{
|
||||
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
|
||||
/// The corresponding POSIX limit is `math.maxInt(isize)`.
|
||||
pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
|
||||
if (bytes.len == 0) return 0;
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.WriteFile(fd, bytes, null, std.io.default_mode);
|
||||
}
|
||||
@@ -1197,6 +1200,7 @@ pub const PWriteError = WriteError || error{Unseekable};
|
||||
/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
|
||||
/// The corresponding POSIX limit is `math.maxInt(isize)`.
|
||||
pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
|
||||
if (bytes.len == 0) return 0;
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
|
||||
}
|
||||
|
||||
@@ -1080,3 +1080,103 @@ test "isatty" {
|
||||
var file = try tmp.dir.createFile("foo", .{});
|
||||
try expectEqual(os.isatty(file.handle), false);
|
||||
}
|
||||
|
||||
test "read with empty buffer" {
|
||||
if (native_os == .wasi) return error.SkipZigTest;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
// Get base abs path
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(allocator, relative_path);
|
||||
};
|
||||
|
||||
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
var file = try fs.cwd().createFile(file_path, .{ .read = true });
|
||||
defer file.close();
|
||||
|
||||
var bytes = try allocator.alloc(u8, 0);
|
||||
|
||||
_ = try os.read(file.handle, bytes);
|
||||
}
|
||||
|
||||
test "pread with empty buffer" {
|
||||
if (native_os == .wasi) return error.SkipZigTest;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
// Get base abs path
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(allocator, relative_path);
|
||||
};
|
||||
|
||||
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
var file = try fs.cwd().createFile(file_path, .{ .read = true });
|
||||
defer file.close();
|
||||
|
||||
var bytes = try allocator.alloc(u8, 0);
|
||||
|
||||
_ = try os.pread(file.handle, bytes, 0);
|
||||
}
|
||||
|
||||
test "write with empty buffer" {
|
||||
if (native_os == .wasi) return error.SkipZigTest;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
// Get base abs path
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(allocator, relative_path);
|
||||
};
|
||||
|
||||
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
var file = try fs.cwd().createFile(file_path, .{});
|
||||
defer file.close();
|
||||
|
||||
var bytes = try allocator.alloc(u8, 0);
|
||||
|
||||
_ = try os.write(file.handle, bytes);
|
||||
}
|
||||
|
||||
test "pwrite with empty buffer" {
|
||||
if (native_os == .wasi) return error.SkipZigTest;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
// Get base abs path
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(allocator, relative_path);
|
||||
};
|
||||
|
||||
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
var file = try fs.cwd().createFile(file_path, .{});
|
||||
defer file.close();
|
||||
|
||||
var bytes = try allocator.alloc(u8, 0);
|
||||
|
||||
_ = try os.pwrite(file.handle, bytes, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user