From 08f0780cb2e33ae4d1f7059b6f173459b46adc25 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Wed, 13 Aug 2025 23:45:23 -0700 Subject: [PATCH] zstd.Decompress.stream: Fix handling of skippable frames in new_frame state The previous code assumed that `initFrame` during the `new_frame` state would always result in the `in_frame` state, but that's not always the case. `initFrame` can also result in the `skippable_frame` state, which would lead to access of union field 'in_frame' while field 'skipping_frame' is active. Now, the switch is re-entered with the updated state so either case is handled appropriately. Fixes the crashes from https://github.com/ziglang/zig/issues/24817 --- lib/std/compress/zstd.zig | 9 +++++++++ lib/std/compress/zstd/Decompress.zig | 11 ++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/std/compress/zstd.zig b/lib/std/compress/zstd.zig index dbfb8fce96..a59df317bb 100644 --- a/lib/std/compress/zstd.zig +++ b/lib/std/compress/zstd.zig @@ -156,3 +156,12 @@ test "declared raw literals size too large" { // block can't be valid as it is a raw literals block. try testExpectDecompressError(error.MalformedLiteralsSection, input_raw); } + +test "skippable frame" { + const input_raw = + "\x50\x2a\x4d\x18" ++ // min magic number for a skippable frame + "\x02\x00\x00\x00" ++ // number of bytes to skip + "\xFF\xFF"; // the bytes that are skipped + + try testExpectDecompress("", input_raw); +} diff --git a/lib/std/compress/zstd/Decompress.zig b/lib/std/compress/zstd/Decompress.zig index 0b5a283567..aacfc70171 100644 --- a/lib/std/compress/zstd/Decompress.zig +++ b/lib/std/compress/zstd/Decompress.zig @@ -155,7 +155,7 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize { const d: *Decompress = @alignCast(@fieldParentPtr("reader", r)); const in = d.input; - switch (d.state) { + state: switch (d.state) { .new_frame => { // Only return EndOfStream when there are exactly 0 bytes remaining on the // frame magic. Any partial magic bytes should be considered a failure. @@ -174,14 +174,7 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize { d.err = err; return error.ReadFailed; }; - return readInFrame(d, w, limit, &d.state.in_frame) catch |err| switch (err) { - error.ReadFailed => return error.ReadFailed, - error.WriteFailed => return error.WriteFailed, - else => |e| { - d.err = e; - return error.ReadFailed; - }, - }; + continue :state d.state; }, .in_frame => |*in_frame| { return readInFrame(d, w, limit, in_frame) catch |err| switch (err) {