authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-04 15:31:08-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-10-04 15:31:08-04:00
logd4ebfa87633aa632a290dfffa38712468e5512db
tree860a4c7741773bd58cc5f8e8bad32395fde7e4cc
parent66204b780699ca6f043d312b4c637c25d6d9f3eb
parent36f1f4f9fe39492367445b60153d7217533fb379
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9880 from squeek502/deflate-construct-errors

deflate: Better Huffman.construct errors and error handling

1 files changed, 48 insertions(+), 7 deletions(-)

lib/std/compress/deflate.zig+48-7
......@@ -45,7 +45,9 @@ const Huffman = struct {
4545
4646 min_code_len: u16,
4747
48 fn construct(self: *Huffman, code_length: []const u16) !void {
48 const ConstructError = error{ Oversubscribed, IncompleteSet };
49
50 fn construct(self: *Huffman, code_length: []const u16) ConstructError!void {
4951 for (self.count) |*val| {
5052 val.* = 0;
5153 }
......@@ -70,7 +72,7 @@ const Huffman = struct {
7072 // Make sure the number of codes with this length isn't too high.
7173 left -= @as(isize, @bitCast(i16, val));
7274 if (left < 0)
73 return error.InvalidTree;
75 return error.Oversubscribed;
7476 }
7577
7678 // Compute the offset of the first symbol represented by a code of a
......@@ -125,6 +127,9 @@ const Huffman = struct {
125127
126128 self.last_code = codes[PREFIX_LUT_BITS + 1];
127129 self.last_index = offset[PREFIX_LUT_BITS + 1] - self.count[PREFIX_LUT_BITS + 1];
130
131 if (left > 0)
132 return error.IncompleteSet;
128133 }
129134};
130135
......@@ -324,7 +329,13 @@ pub fn InflateStream(comptime ReaderType: type) type {
324329 try lencode.construct(len_lengths[0..]);
325330
326331 const dist_lengths = [_]u16{5} ** MAXDCODES;
327 try distcode.construct(dist_lengths[0..]);
332 distcode.construct(dist_lengths[0..]) catch |err| switch (err) {
333 // This error is expected because we only compute distance codes
334 // 0-29, which is fine since "distance codes 30-31 will never actually
335 // occur in the compressed data" (from section 3.2.6 of RFC1951).
336 error.IncompleteSet => {},
337 else => return err,
338 };
328339 }
329340
330341 self.hlen = &lencode;
......@@ -359,7 +370,7 @@ pub fn InflateStream(comptime ReaderType: type) type {
359370 lengths[val] = @intCast(u16, try self.readBits(3));
360371 }
361372
362 try lencode.construct(lengths[0..]);
373 lencode.construct(lengths[0..]) catch return error.InvalidTree;
363374 }
364375
365376 // Read the length/literal and distance code length tables.
......@@ -408,8 +419,24 @@ pub fn InflateStream(comptime ReaderType: type) type {
408419 if (lengths[256] == 0)
409420 return error.MissingEOBCode;
410421
411 try self.huffman_tables[0].construct(lengths[0..nlen]);
412 try self.huffman_tables[1].construct(lengths[nlen .. nlen + ndist]);
422 self.huffman_tables[0].construct(lengths[0..nlen]) catch |err| switch (err) {
423 error.Oversubscribed => return error.InvalidTree,
424 error.IncompleteSet => {
425 // incomplete code ok only for single length 1 code
426 if (nlen != self.huffman_tables[0].count[0] + self.huffman_tables[0].count[1]) {
427 return error.InvalidTree;
428 }
429 },
430 };
431 self.huffman_tables[1].construct(lengths[nlen .. nlen + ndist]) catch |err| switch (err) {
432 error.Oversubscribed => return error.InvalidTree,
433 error.IncompleteSet => {
434 // incomplete code ok only for single length 1 code
435 if (ndist != self.huffman_tables[1].count[0] + self.huffman_tables[1].count[1]) {
436 return error.InvalidTree;
437 }
438 },
439 };
413440
414441 self.hlen = &self.huffman_tables[0];
415442 self.hdist = &self.huffman_tables[1];
......@@ -684,8 +711,22 @@ test "distance past beginning of output stream" {
684711
685712test "inflateStream fuzzing" {
686713 // see https://github.com/ziglang/zig/issues/9842
687 try std.testing.expectError(error.EndOfStream, testInflate("\x950000"));
714 try std.testing.expectError(error.EndOfStream, testInflate("\x95\x90=o\xc20\x10\x86\xf30"));
688715 try std.testing.expectError(error.OutOfCodes, testInflate("\x950\x00\x0000000"));
716
717 // Huffman.construct errors
718 // lencode
719 try std.testing.expectError(error.InvalidTree, testInflate("\x950000"));
720 try std.testing.expectError(error.InvalidTree, testInflate("\x05000"));
721 // hlen
722 try std.testing.expectError(error.InvalidTree, testInflate("\x05\xea\x01\t\x00\x00\x00\x01\x00\\\xbf.\t\x00"));
723 // hdist
724 try std.testing.expectError(error.InvalidTree, testInflate("\x05\xe0\x01A\x00\x00\x00\x00\x10\\\xbf."));
725
726 // Huffman.construct -> error.IncompleteSet returns that shouldn't give error.InvalidTree
727 // (like the "empty distance alphabet" test but for ndist instead of nlen)
728 try std.testing.expectError(error.EndOfStream, testInflate("\x05\xe0\x01\t\x00\x00\x00\x00\x10\\\xbf\xce"));
729 try testInflate("\x15\xe0\x01\t\x00\x00\x00\x00\x10\\\xbf.0");
689730}
690731
691732fn testInflate(data: []const u8) !void {