authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-13 18:23:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-15 10:48:15-07:00
loge098b287e18b8a7a4df0fdb48d32fb4376daba07
treeae36d182b8fd3e3b8ee06dad0558fa3c08c4a52f
parentb1299d515351acbfa1f169c8e65a3fa2b3e39f1a

std.fs.File.writevAll: fix behavior for 0-length vectors

The OS layer expects pointer addresses to be inside the application's address space even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer addresses when the length is zero. So this function now modifies the iov_base fields when the length is zero. This is a companion commit to b4893eb05565b2cb033c6ed88617d73faf878455.

2 files changed, 20 insertions(+), 4 deletions(-)

lib/std/fs/file.zig+15-2
......@@ -1196,13 +1196,26 @@ pub const File = struct {
11961196 }
11971197 }
11981198
1199 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1200 /// order to handle partial writes from the underlying OS layer.
1199 /// The `iovecs` parameter is mutable because:
1200 /// * This function needs to mutate the fields in order to handle partial
1201 /// writes from the underlying OS layer.
1202 /// * The OS layer expects pointer addresses to be inside the application's address space
1203 /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer
1204 /// addresses when the length is zero. So this function modifies the iov_base fields
1205 /// when the length is zero.
12011206 /// See https://github.com/ziglang/zig/issues/7699
12021207 /// See equivalent function: `std.net.Stream.writevAll`.
12031208 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
12041209 if (iovecs.len == 0) return;
12051210
1211 // We use the address of this local variable for all zero-length
1212 // vectors so that the OS does not complain that we are giving it
1213 // addresses outside the application's address space.
1214 var garbage: [1]u8 = undefined;
1215 for (iovecs) |*v| {
1216 if (v.iov_len == 0) v.iov_base = &garbage;
1217 }
1218
12061219 var i: usize = 0;
12071220 while (true) {
12081221 var amt = try self.writev(iovecs[i..]);
lib/std/os.zig+5-2
......@@ -767,8 +767,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
767767/// * Windows
768768/// On these systems, the read races with concurrent writes to the same file descriptor.
769769///
770/// This function assumes that all zero-length vectors have a pointer within the address
771/// space of the application.
770/// This function assumes that all vectors, including zero-length vectors, have
771/// a pointer within the address space of the application.
772772pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
773773 if (builtin.os.tag == .windows) {
774774 // TODO improve this to use ReadFileScatter
......@@ -1170,6 +1170,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
11701170/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
11711171///
11721172/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
1173///
1174/// This function assumes that all vectors, including zero-length vectors, have
1175/// a pointer within the address space of the application.
11731176pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
11741177 if (builtin.os.tag == .windows) {
11751178 // TODO improve this to use WriteFileScatter