Skip to content

deflate.InflateStream bits_left unsafety #9842

@squeek502

Description

@squeek502

With certain inputs, it's possible for the bits_left field of InflateStream to either cause:

thread 445840 panic: integer cast truncated bits
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:276:48: 0x22f215 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).peekBits (fuzz-deflate-debug)
                self.bits |= @as(u32, byte) << @intCast(u5, self.bits_left);
                                               ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:491:38: 0x2305e6 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).decode (fuzz-deflate-debug)
                _ = try self.peekBits(code_len);
                                     ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:368:47: 0x22db87 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).dynamic (fuzz-deflate-debug)
                const symbol = try self.decode(&lencode);
                                              ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:554:50: 0x20f8b3 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).step (fuzz-deflate-debug)
                            2 => try self.dynamic(),
                                                 ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:635:30: 0x20f109 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).read (fuzz-deflate-debug)
                try self.step();
                             ^

or

thread 445832 panic: integer overflow
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:289:28: 0x22f351 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).discardBits (fuzz-deflate-debug)
            self.bits_left -= bits;
                           ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:284:29: 0x22d749 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).readBits (fuzz-deflate-debug)
            self.discardBits(bits);
                            ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:461:60: 0x22ebb2 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).codes (fuzz-deflate-debug)
                            @intCast(u16, try self.readBits(DEXT[distance_symbol]));
                                                           ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:559:44: 0x20f973 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).step (fuzz-deflate-debug)
                        if (!try self.codes(self.hlen, self.hdist)) {
                                           ^
/home/ryan/Programming/zig/zig/build/lib/zig/std/compress/deflate.zig:635:30: 0x20f109 in std.compress.deflate.InflateStream(std.io.reader.Reader(*std.io.fixed_buffer_stream.FixedBufferStream([]u8),std.io.fixed_buffer_stream.ReadError,std.io.fixed_buffer_stream.FixedBufferStream([]u8).read)).read (fuzz-deflate-debug)
                try self.step();
                             ^

This was found by fuzzing std.compress.deflate.inflateStream().reader().readAllAlloc().

Here's some tests that trigger both crashes with minimized inputs:

const std = @import("std");

test "integer overflow" {
    const data = "\x950000";
    try testInflate(data);
}

test "intCast truncate" {
    const data = "\x950\x00\x0000000";
    try testInflate(data);
}

fn testInflate(data: []const u8) !void {
    const allocator = std.testing.allocator;

    const reader = std.io.fixedBufferStream(data).reader();
    var window: [0x8000]u8 = undefined;
    var inflate = std.compress.deflate.inflateStream(reader, &window);

    var inflated = inflate.reader().readAllAlloc(allocator, std.math.maxInt(usize)) catch {
        return;
    };
    defer allocator.free(inflated);
}

And here's a zip of a larger set of inputs that will trigger the crashes (potentially in different ways):

inflate-stream-crash-inputs.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviorstandard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions