authorgravatar for readcuttingt@gmail.comTom Read Cutting <readcuttingt@gmail.com> 2022-12-11 11:53:52+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-12 15:10:59-05:00
logd0eef26687ca2b4b206b70f001741314d4b8bae8
tree412effd210ef65092381783da7397e4376a7280a
parent09cc6b4b9e76434d734ed131ed48ca849bf8a6ba

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.

2 files changed, 104 insertions(+), 0 deletions(-)

lib/std/os.zig+4
......@@ -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)`.
662662pub 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)`.
789790pub 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)`.
10471049pub 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)`.
11991202pub 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+100
......@@ -1068,3 +1068,103 @@ test "isatty" {
10681068 var file = try tmp.dir.createFile("foo", .{});
10691069 try expectEqual(os.isatty(file.handle), false);
10701070}
1071
1072test "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
1097test "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
1122test "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
1147test "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}