authorgravatar for achan1989@gmail.comachan1989 <achan1989@gmail.com> 2025-10-05 14:26:48+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-07 09:53:34-07:00
log27aba2d776caf59bb6569934626af587fdba9c75
treeaa17ab3a3cb980b718574e62d665638858764db2
parent163ebe044b76ada70b2bee2e17b9f3e948d54754

Fix infinite loop in Reader.Limited


1 files changed, 63 insertions(+), 3 deletions(-)

lib/std/Io/Reader/Limited.zig+63-3
...@@ -28,9 +28,11 @@ pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited {...@@ -28,9 +28,11 @@ pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited {
28fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {28fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
29 const l: *Limited = @fieldParentPtr("interface", r);29 const l: *Limited = @fieldParentPtr("interface", r);
30 const combined_limit = limit.min(l.remaining);30 const combined_limit = limit.min(l.remaining);
31 const n = try l.unlimited.stream(w, combined_limit);31 if (combined_limit.nonzero()) {
32 l.remaining = l.remaining.subtract(n).?;32 const n = try l.unlimited.stream(w, combined_limit);
33 return n;33 l.remaining = l.remaining.subtract(n).?;
34 return n;
35 } else return error.EndOfStream;
34}36}
3537
36test stream {38test stream {
...@@ -49,6 +51,64 @@ test stream {...@@ -49,6 +51,64 @@ test stream {
49 try std.testing.expectEqualStrings("test", result_buf[0..streamed]);51 try std.testing.expectEqualStrings("test", result_buf[0..streamed]);
50}52}
5153
54test "readSliceAll from infinite source" {
55 const InfSource = struct {
56 reader: std.Io.Reader,
57
58 pub fn init(buffer: []u8) @This() {
59 return @This(){
60 .reader = .{
61 .vtable = &.{
62 .stream = streamA,
63 },
64 .buffer = buffer,
65 .seek = 0,
66 .end = 0,
67 },
68 };
69 }
70
71 fn streamA(io_reader: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
72 _ = io_reader;
73
74 std.debug.assert(limit.nonzero());
75
76 const n_bytes_remaining = limit.minInt(2);
77 for (0..n_bytes_remaining) |_| {
78 try w.writeByte('A');
79 }
80 return n_bytes_remaining;
81 }
82 };
83
84 // Exact size
85 {
86 var inf_buf: [10]u8 = undefined;
87 var inf_stream = InfSource.init(&inf_buf);
88
89 var limit_buf: [2]u8 = undefined;
90 var limited: std.Io.Reader.Limited = .init(&inf_stream.reader, .limited(2), &limit_buf);
91 const limited_reader = &limited.interface;
92
93 var out_buffer: [2]u8 = undefined;
94 try std.testing.expectEqual({}, limited_reader.readSliceAll(&out_buffer));
95 try std.testing.expectEqualStrings("AA", &out_buffer);
96 }
97
98 // Too large
99 {
100 var inf_buf: [10]u8 = undefined;
101 var inf_stream = InfSource.init(&inf_buf);
102
103 var limit_buf: [2]u8 = undefined;
104 var limited: std.Io.Reader.Limited = .init(&inf_stream.reader, .limited(2), &limit_buf);
105 const limited_reader = &limited.interface;
106
107 var out_buffer: [8]u8 = undefined;
108 try std.testing.expectError(error.EndOfStream, limited_reader.readSliceAll(&out_buffer));
109 }
110}
111
52fn discard(r: *Reader, limit: Limit) Reader.Error!usize {112fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
53 const l: *Limited = @fieldParentPtr("interface", r);113 const l: *Limited = @fieldParentPtr("interface", r);
54 const combined_limit = limit.min(l.remaining);114 const combined_limit = limit.min(l.remaining);