@@ -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 ("\x95 0000 " ));
714+ try std .testing .expectError (error .EndOfStream , testInflate ("\x95\x90 =o \xc2 0 \x10\x86\xf3 0 " ));
688715 try std .testing .expectError (error .OutOfCodes , testInflate ("\x95 0\x00\x00 00000" ));
716+
717+ // Huffman.construct errors
718+ // lencode
719+ try std .testing .expectError (error .InvalidTree , testInflate ("\x95 0000" ));
720+ try std .testing .expectError (error .InvalidTree , testInflate ("\x05 000" ));
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\x01 A\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 {
0 commit comments