@@ -45,7 +45,9 @@ const Huffman = struct {
45
45
46
46
min_code_len : u16 ,
47
47
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 {
49
51
for (self .count ) | * val | {
50
52
val .* = 0 ;
51
53
}
@@ -70,7 +72,7 @@ const Huffman = struct {
70
72
// Make sure the number of codes with this length isn't too high.
71
73
left -= @as (isize , @bitCast (i16 , val ));
72
74
if (left < 0 )
73
- return error .InvalidTree ;
75
+ return error .Oversubscribed ;
74
76
}
75
77
76
78
// Compute the offset of the first symbol represented by a code of a
@@ -125,6 +127,9 @@ const Huffman = struct {
125
127
126
128
self .last_code = codes [PREFIX_LUT_BITS + 1 ];
127
129
self .last_index = offset [PREFIX_LUT_BITS + 1 ] - self .count [PREFIX_LUT_BITS + 1 ];
130
+
131
+ if (left > 0 )
132
+ return error .IncompleteSet ;
128
133
}
129
134
};
130
135
@@ -324,7 +329,13 @@ pub fn InflateStream(comptime ReaderType: type) type {
324
329
try lencode .construct (len_lengths [0.. ]);
325
330
326
331
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
+ };
328
339
}
329
340
330
341
self .hlen = & lencode ;
@@ -359,7 +370,7 @@ pub fn InflateStream(comptime ReaderType: type) type {
359
370
lengths [val ] = @intCast (u16 , try self .readBits (3 ));
360
371
}
361
372
362
- try lencode .construct (lengths [0.. ]);
373
+ lencode .construct (lengths [0.. ]) catch return error . InvalidTree ;
363
374
}
364
375
365
376
// Read the length/literal and distance code length tables.
@@ -408,8 +419,24 @@ pub fn InflateStream(comptime ReaderType: type) type {
408
419
if (lengths [256 ] == 0 )
409
420
return error .MissingEOBCode ;
410
421
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
+ };
413
440
414
441
self .hlen = & self .huffman_tables [0 ];
415
442
self .hdist = & self .huffman_tables [1 ];
@@ -684,8 +711,22 @@ test "distance past beginning of output stream" {
684
711
685
712
test "inflateStream fuzzing" {
686
713
// 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 " ));
688
715
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" );
689
730
}
690
731
691
732
fn testInflate (data : []const u8 ) ! void {
0 commit comments