Commit 1e805df81d

Matthew Borkowski <matthew.h.borkowski@gmail.com>
2021-09-27 20:06:14
deflate.zig: fix bits_left overflow at EndOfStream and @intCast truncation with empty Huffman table
1 parent 754ea11
Changed files (1)
lib
std
compress
lib/std/compress/deflate.zig
@@ -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| {
@@ -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;
         }
@@ -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
@@ -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..]));
+}
+
+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 };
+    try testInflate(stream[0..]);
+}
 
-    const reader = std.io.fixedBufferStream(&stream).reader();
+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 = inflateStream(reader, &window);
-
-    var buf: [1]u8 = undefined;
-    try std.testing.expectError(error.InvalidLength, inflate.read(&buf));
+    var inflated = try inflate.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize));
+    defer std.testing.allocator.free(inflated);
 }