Commit e098b287e1

Andrew Kelley <andrew@ziglang.org>
2023-03-14 02:23:17
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.
1 parent b1299d5
Changed files (2)
lib
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..]);
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