Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deflate.zig: fix bits_left overflow at EndOfStream and @intCast truncation with empty Huffman table #9848

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 27 additions & 7 deletions lib/std/compress/deflate.zig
Expand Up @@ -58,8 +58,10 @@ const Huffman = struct {
}

// All zero.
if (self.count[0] == code_length.len)
if (self.count[0] == code_length.len) {
self.min_code_len = 0;
return;
}

var left: isize = 1;
for (self.count[1..]) |val| {
Expand Down Expand Up @@ -280,7 +282,7 @@ pub fn InflateStream(comptime ReaderType: type) type {
return self.bits & mask;
}
fn readBits(self: *Self, bits: usize) !u32 {
const val = self.peekBits(bits);
const val = try self.peekBits(bits);
self.discardBits(bits);
return val;
}
Expand Down Expand Up @@ -487,6 +489,8 @@ pub fn InflateStream(comptime ReaderType: type) type {
// We can't read PREFIX_LUT_BITS as we don't want to read past the
// deflate stream end, use an incremental approach instead.
var code_len = h.min_code_len;
if (code_len == 0)
return error.OutOfCodes;
while (true) {
_ = try self.peekBits(code_len);
// Small optimization win, use as many bits as possible in the
Expand Down Expand Up @@ -658,11 +662,27 @@ test "lengths overflow" {
// f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6
// 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11
const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 };
try std.testing.expectError(error.InvalidLength, testInflate(stream[0..]));
}

const reader = std.io.fixedBufferStream(&stream).reader();
var window: [0x8000]u8 = undefined;
var inflate = inflateStream(reader, &window);
test "empty distance alphabet" {
// dynamic block with empty distance alphabet is valid if end of data symbol is used immediately
// f dy hlit hdist hclen 16 17 18 0 8 7 9 6 10 5 11 4 12 3 13 2 14 1 15 (18) x128 (18) x128 (1) ( 0) (256)
// 1 10 00000 00000 1111 000 000 010 010 000 000 000 000 000 000 000 000 000 000 000 000 000 001 000 (11) 1110101 (11) 1110101 (0) (10) (0)
const stream = [_]u8 { 0b00000101, 0b11100000, 0b00000001, 0b00001001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00010000, 0b01011100, 0b10111111, 0b00101110 };
mattbork marked this conversation as resolved.
Show resolved Hide resolved
try testInflate(stream[0..]);
}

var buf: [1]u8 = undefined;
try std.testing.expectError(error.InvalidLength, inflate.read(&buf));
test "inflateStream fuzzing" {
// see https://github.com/ziglang/zig/issues/9842
try std.testing.expectError(error.EndOfStream, testInflate("\x950000"));
try std.testing.expectError(error.OutOfCodes, testInflate("\x950\x00\x0000000"));
}

fn testInflate(data: []const u8) !void {
var window: [0x8000]u8 = undefined;
const reader = std.io.fixedBufferStream(data).reader();
var inflate = std.compress.deflate.inflateStream(reader, &window);
var inflated = try inflate.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize));
defer std.testing.allocator.free(inflated);
}