authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-14 16:08:06-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-08-14 16:08:06-07:00
loge252e6c696192729e576fffbd26991bd4b19e434
tree481c8137999a5a650066e2d5229b21b69cf23e76
parent2761cc8be0ef89772a41cb0aaf512ac463aa5c23
parent353cf1f671b9127e7f425aaf3dcba655117ee45b
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24847 from squeek502/zstd-partial-magic

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

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

lib/std/compress/zstd.zig+6
...@@ -121,6 +121,12 @@ test Decompress {...@@ -121,6 +121,12 @@ test Decompress {
121 try testExpectDecompress(uncompressed, compressed19);121 try testExpectDecompress(uncompressed, compressed19);
122}122}
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
124test "zero sized raw block" {130test "zero sized raw block" {
125 const input_raw =131 const input_raw =
126 "\x28\xb5\x2f\xfd" ++ // zstandard frame magic number132 "\x28\xb5\x2f\xfd" ++ // zstandard frame magic number
lib/std/compress/zstd/Decompress.zig+12-3
...@@ -17,7 +17,6 @@ const State = union(enum) {...@@ -17,7 +17,6 @@ const State = union(enum) {
17 new_frame,17 new_frame,
18 in_frame: InFrame,18 in_frame: InFrame,
19 skipping_frame: usize,19 skipping_frame: usize,
20 end,
2120
22 const InFrame = struct {21 const InFrame = struct {
23 frame: Frame,22 frame: Frame,
...@@ -158,7 +157,18 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {...@@ -158,7 +157,18 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
158157
159 switch (d.state) {158 switch (d.state) {
160 .new_frame => {159 .new_frame => {
161 // Allow error.EndOfStream only on the frame magic.160 // Only return EndOfStream when there are exactly 0 bytes remaining on the
161 // frame magic. Any partial magic bytes should be considered a failure.
162 in.fill(@sizeOf(Frame.Magic)) catch |err| switch (err) {
163 error.EndOfStream => {
164 if (in.bufferedLen() != 0) {
165 d.err = error.BadMagic;
166 return error.ReadFailed;
167 }
168 return err;
169 },
170 else => |e| return e,
171 };
162 const magic = try in.takeEnumNonexhaustive(Frame.Magic, .little);172 const magic = try in.takeEnumNonexhaustive(Frame.Magic, .little);
163 initFrame(d, w.buffer.len, magic) catch |err| {173 initFrame(d, w.buffer.len, magic) catch |err| {
164 d.err = err;174 d.err = err;
...@@ -192,7 +202,6 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {...@@ -192,7 +202,6 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
192 if (remaining.* == 0) d.state = .new_frame;202 if (remaining.* == 0) d.state = .new_frame;
193 return 0;203 return 0;
194 },204 },
195 .end => return error.EndOfStream,
196 }205 }
197}206}
198207