From e098b287e18b8a7a4df0fdb48d32fb4376daba07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 18:23:17 -0700 Subject: [PATCH] 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. --- lib/std/fs/file.zig | 17 +++++++++++++++-- lib/std/os.zig | 7 +++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index f81841a662..8235b8aecf 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -1196,13 +1196,26 @@ pub const File = struct { } } - /// The `iovecs` parameter is mutable because this function needs to mutate the fields in - /// order to handle partial writes from the underlying OS layer. + /// The `iovecs` parameter is mutable because: + /// * This function needs to mutate the fields in order to handle partial + /// writes from the underlying OS layer. + /// * 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 modifies the iov_base fields + /// when the length is zero. /// See https://github.com/ziglang/zig/issues/7699 /// See equivalent function: `std.net.Stream.writevAll`. pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { if (iovecs.len == 0) return; + // We use the address of this local variable for all zero-length + // vectors so that the OS does not complain that we are giving it + // addresses outside the application's address space. + var garbage: [1]u8 = undefined; + for (iovecs) |*v| { + if (v.iov_len == 0) v.iov_base = &garbage; + } + var i: usize = 0; while (true) { var amt = try self.writev(iovecs[i..]); diff --git a/lib/std/os.zig b/lib/std/os.zig index b11aac493f..722028b3f8 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -767,8 +767,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { /// * Windows /// On these systems, the read races with concurrent writes to the same file descriptor. /// -/// This function assumes that all zero-length vectors have a pointer within the address -/// space of the application. +/// This function assumes that all vectors, including zero-length vectors, have +/// a pointer within the address space of the application. pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { if (builtin.os.tag == .windows) { // TODO improve this to use ReadFileScatter @@ -1170,6 +1170,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur. +/// +/// This function assumes that all vectors, including zero-length vectors, have +/// a pointer within the address space of the application. pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { if (builtin.os.tag == .windows) { // TODO improve this to use WriteFileScatter