authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-08-14 01:12:58-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-08-14 14:08:49-07:00
log60b0b2129664d6e97822c8ce815518c6a48f9203
tree451da90a263939d8aa45e9e6f0dedf7cdaf276fe
parent59de7e3a5725f0c4eeb64bc464db85e019af7446

zstd.Decompress: Treat a partial magic number as a failure

Previously, the "allow EndOfStream" part of this logic was too permissive. If there are a few dangling bytes at the end of the stream, that should be treated as a bad magic number. The only case where EndOfStream is allowed is when the stream is truly at the end, with exactly zero bytes available.

2 files changed, 18 insertions(+), 1 deletions(-)

lib/std/compress/zstd.zig+6
......@@ -121,6 +121,12 @@ test Decompress {
121121 try testExpectDecompress(uncompressed, compressed19);
122122}
123123
124test "partial magic number" {
125 const input_raw =
126 "\x28\xb5\x2f"; // 3 bytes of the 4-byte zstandard frame magic number
127 try testExpectDecompressError(error.BadMagic, input_raw);
128}
129
124130test "zero sized raw block" {
125131 const input_raw =
126132 "\x28\xb5\x2f\xfd" ++ // zstandard frame magic number
lib/std/compress/zstd/Decompress.zig+12-1
......@@ -158,7 +158,18 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
158158
159159 switch (d.state) {
160160 .new_frame => {
161 // Allow error.EndOfStream only on the frame magic.
161 // Only return EndOfStream when there are exactly 0 bytes remaining on the
162 // frame magic. Any partial magic bytes should be considered a failure.
163 in.fill(@sizeOf(Frame.Magic)) catch |err| switch (err) {
164 error.EndOfStream => {
165 if (in.bufferedLen() != 0) {
166 d.err = error.BadMagic;
167 return error.ReadFailed;
168 }
169 return err;
170 },
171 else => |e| return e,
172 };
162173 const magic = try in.takeEnumNonexhaustive(Frame.Magic, .little);
163174 initFrame(d, w.buffer.len, magic) catch |err| {
164175 d.err = err;