Commit 3fbb88c4bd

Ryan Liptak <squeek502@hotmail.com>
2025-09-20 07:10:53
Reader.defaultReadVec: Workaround bad `r.end += r.vtable.stream()` behavior
If `r.end` is updated in the `stream` implementation, then it's possible that `r.end += ...` will behave unexpectedly. What seems to happen is that it reverts back to its value before the function call and then the increment happens. Here's a reproduction: ```zig test "fill when stream modifies `end` and returns 0" { var buf: [3]u8 = undefined; var zero_reader = infiniteZeroes(&buf); _ = try zero_reader.fill(1); try std.testing.expectEqual(buf.len, zero_reader.end); } pub fn infiniteZeroes(buf: []u8) std.Io.Reader { return .{ .vtable = &.{ .stream = stream, }, .buffer = buf, .end = 0, .seek = 0, }; } fn stream(r: *std.Io.Reader, _: *std.Io.Writer, _: std.Io.Limit) std.Io.Reader.StreamError!usize { @memset(r.buffer[r.seek..], 0); r.end = r.buffer.len; return 0; } ``` When `fill` is called, it will call into `vtable.readVec` which in this case is `defaultReadVec`. In `defaultReadVec`: - Before the `r.end += r.vtable.stream` line, `r.end` will be 0 - In `r.vtable.stream`, `r.end` is modified to 3 and it returns 0 - After the `r.end += r.vtable.stream` line, `r.end` will be 0 instead of the expected 3 Separating the `r.end += stream();` into two lines fixes the problem (and this separation is done elsewhere in `Reader` so it seems possible that this class of bug has been encountered before). Potentially related issues: - https://github.com/ziglang/zig/issues/4021 - https://github.com/ziglang/zig/issues/12064
1 parent 0e47bd1
Changed files (1)
lib
std
lib/std/Io/Reader.zig
@@ -432,10 +432,11 @@ pub fn defaultReadVec(r: *Reader, data: [][]u8) Error!usize {
         .vtable = &.{ .drain = Writer.fixedDrain },
     };
     const limit: Limit = .limited(writer.buffer.len - writer.end);
-    r.end += r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
+    const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
         error.WriteFailed => unreachable,
         else => |e| return e,
     };
+    r.end += n;
     return 0;
 }