From 25c3878c00b92ffc884d89d32817ca9c244f7972 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 18:06:56 -0700 Subject: [PATCH] std.fs.File.readvAll: 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. --- lib/std/fs/file.zig | 21 ++++++++++++++++++--- lib/std/os.zig | 3 +++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index bf93a61239..f81841a662 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -1048,12 +1048,27 @@ pub const File = struct { /// Returns the number of bytes read. If the number read is smaller than the total bytes /// from all the buffers, it means the file reached the end. Reaching the end of a file /// is not an error condition. - /// The `iovecs` parameter is mutable because this function needs to mutate the fields in - /// order to handle partial reads from the underlying OS layer. - /// See https://github.com/ziglang/zig/issues/7699 + /// + /// The `iovecs` parameter is mutable because: + /// * This function needs to mutate the fields in order to handle partial + /// reads 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. + /// + /// Related open issue: https://github.com/ziglang/zig/issues/7699 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { if (iovecs.len == 0) return 0; + // 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; var off: usize = 0; while (true) { diff --git a/lib/std/os.zig b/lib/std/os.zig index 9d5625c13e..b11aac493f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -766,6 +766,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { /// This operation is non-atomic on the following systems: /// * 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. pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { if (builtin.os.tag == .windows) { // TODO improve this to use ReadFileScatter