Skip to content

Commit b241665

Browse files
committed
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.
1 parent 9bcfe55 commit b241665

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

lib/std/os.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ pub const ReadError = error{
660660
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
661661
/// The corresponding POSIX limit is `math.maxInt(isize)`.
662662
pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
663+
if (buf.len == 0) return 0;
663664
if (builtin.os.tag == .windows) {
664665
return windows.ReadFile(fd, buf, null, std.io.default_mode);
665666
}
@@ -787,6 +788,7 @@ pub const PReadError = ReadError || error{Unseekable};
787788
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
788789
/// The corresponding POSIX limit is `math.maxInt(isize)`.
789790
pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
791+
if (buf.len == 0) return 0;
790792
if (builtin.os.tag == .windows) {
791793
return windows.ReadFile(fd, buf, offset, std.io.default_mode);
792794
}
@@ -1045,6 +1047,7 @@ pub const WriteError = error{
10451047
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
10461048
/// The corresponding POSIX limit is `math.maxInt(isize)`.
10471049
pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
1050+
if (bytes.len == 0) return 0;
10481051
if (builtin.os.tag == .windows) {
10491052
return windows.WriteFile(fd, bytes, null, std.io.default_mode);
10501053
}
@@ -1197,6 +1200,7 @@ pub const PWriteError = WriteError || error{Unseekable};
11971200
/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
11981201
/// The corresponding POSIX limit is `math.maxInt(isize)`.
11991202
pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
1203+
if (bytes.len == 0) return 0;
12001204
if (builtin.os.tag == .windows) {
12011205
return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
12021206
}

lib/std/os/test.zig

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,107 @@ test "isatty" {
10731073
var file = try tmp.dir.createFile("foo", .{});
10741074
try expectEqual(os.isatty(file.handle), false);
10751075
}
1076+
1077+
test "read with empty buffer" {
1078+
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1079+
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
1080+
1081+
var tmp = tmpDir(.{});
1082+
defer tmp.cleanup();
1083+
1084+
var arena = ArenaAllocator.init(testing.allocator);
1085+
defer arena.deinit();
1086+
const allocator = arena.allocator();
1087+
1088+
// Get base abs path
1089+
const base_path = blk: {
1090+
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1091+
break :blk try fs.realpathAlloc(allocator, relative_path);
1092+
};
1093+
1094+
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1095+
var file = try fs.cwd().createFile(file_path, .{ .read = true });
1096+
defer file.close();
1097+
1098+
var bytes = try allocator.alloc(u8, 0);
1099+
1100+
_ = try os.read(file.handle, bytes);
1101+
}
1102+
1103+
test "pread with empty buffer" {
1104+
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1105+
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
1106+
1107+
var tmp = tmpDir(.{});
1108+
defer tmp.cleanup();
1109+
1110+
var arena = ArenaAllocator.init(testing.allocator);
1111+
defer arena.deinit();
1112+
const allocator = arena.allocator();
1113+
1114+
// Get base abs path
1115+
const base_path = blk: {
1116+
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1117+
break :blk try fs.realpathAlloc(allocator, relative_path);
1118+
};
1119+
1120+
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1121+
var file = try fs.cwd().createFile(file_path, .{ .read = true });
1122+
defer file.close();
1123+
1124+
var bytes = try allocator.alloc(u8, 0);
1125+
1126+
_ = try os.pread(file.handle, bytes, 0);
1127+
}
1128+
1129+
test "write with empty buffer" {
1130+
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1131+
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
1132+
1133+
var tmp = tmpDir(.{});
1134+
defer tmp.cleanup();
1135+
1136+
var arena = ArenaAllocator.init(testing.allocator);
1137+
defer arena.deinit();
1138+
const allocator = arena.allocator();
1139+
1140+
// Get base abs path
1141+
const base_path = blk: {
1142+
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1143+
break :blk try fs.realpathAlloc(allocator, relative_path);
1144+
};
1145+
1146+
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1147+
var file = try fs.cwd().createFile(file_path, .{});
1148+
defer file.close();
1149+
1150+
var bytes = try allocator.alloc(u8, 0);
1151+
1152+
_ = try os.write(file.handle, bytes);
1153+
}
1154+
1155+
test "pwrite with empty buffer" {
1156+
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1157+
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
1158+
1159+
var tmp = tmpDir(.{});
1160+
defer tmp.cleanup();
1161+
1162+
var arena = ArenaAllocator.init(testing.allocator);
1163+
defer arena.deinit();
1164+
const allocator = arena.allocator();
1165+
1166+
// Get base abs path
1167+
const base_path = blk: {
1168+
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1169+
break :blk try fs.realpathAlloc(allocator, relative_path);
1170+
};
1171+
1172+
var file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1173+
var file = try fs.cwd().createFile(file_path, .{});
1174+
defer file.close();
1175+
1176+
var bytes = try allocator.alloc(u8, 0);
1177+
1178+
_ = try os.pwrite(file.handle, bytes, 0);
1179+
}

0 commit comments

Comments
 (0)