Skip to content

Commit d0eef26

Browse files
moosichuandrewrk
authored andcommitted
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 09cc6b4 commit d0eef26

File tree

2 files changed

+104
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)