Skip to content

Commit

Permalink
fix bugs found by fuzz testing
Browse files Browse the repository at this point in the history
Thanks to @squeek502
Fixes #6

Results of running:

zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000000,sig:06,src:000015,time:4,execs:181,op:havoc,rep:9
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000001,sig:06,src:000015,time:9,execs:226,op:havoc,rep:5
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000002,sig:06,src:000015,time:9,execs:227,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000003,sig:06,src:000015,time:13,execs:253,op:havoc,rep:11
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000004,sig:06,src:000015,time:15,execs:266,op:havoc,rep:12
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000005,sig:06,src:000015,time:17,execs:286,op:havoc,rep:10
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000006,sig:06,src:000015,time:20,execs:304,op:havoc,rep:9
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000007,sig:06,src:000015,time:36,execs:432,op:havoc,rep:13
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000008,sig:06,src:000015,time:44,execs:489,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000009,sig:06,src:000015,time:48,execs:519,op:havoc,rep:4
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000010,sig:06,src:000015,time:62,execs:616,op:havoc,rep:15
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000011,sig:06,src:000015,time:69,execs:672,op:havoc,rep:7
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000012,sig:06,src:000015,time:74,execs:706,op:havoc,rep:12
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000013,sig:06,src:000015,time:105,execs:933,op:havoc,rep:12
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000014,sig:06,src:000015,time:207,execs:1675,op:havoc,rep:8
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000015,sig:06,src:000015,time:394,execs:3136,op:havoc,rep:6
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000016,sig:06,src:000015,time:523,execs:4139,op:havoc,rep:15
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000017,sig:06,src:000015,time:635,execs:5009,op:havoc,rep:11
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000018,sig:06,src:000015,time:741,execs:5849,op:havoc,rep:7
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000019,sig:06,src:000015,time:754,execs:5950,op:havoc,rep:16
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000020,sig:06,src:000015,time:923,execs:7270,op:havoc,rep:8
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000021,sig:06,src:000015,time:1161,execs:9137,op:havoc,rep:10
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000022,sig:06,src:000015,time:1313,execs:10337,op:havoc,rep:11
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000023,sig:06,src:000133,time:1808,execs:14219,op:havoc,rep:1
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000024,sig:06,src:000133,time:3011,execs:23273,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000025,sig:06,src:000002,time:3715,execs:28624,op:havoc,rep:4
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000026,sig:06,src:000002,time:3759,execs:28948,op:havoc,rep:1
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000027,sig:06,src:000002,time:3759,execs:28949,op:havoc,rep:1
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000028,sig:06,src:000002,time:4027,execs:30894,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000029,sig:06,src:000002,time:4067,execs:31179,op:havoc,rep:3
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000030,sig:06,src:000002,time:4327,execs:33095,op:havoc,rep:1
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000031,sig:06,src:000002,time:4718,execs:35898,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000032,sig:06,src:000002,time:5053,execs:38294,op:havoc,rep:4
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000033,sig:06,src:000002,time:5378,execs:40657,op:havoc,rep:4
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000034,sig:06,src:000086+000178,time:5746,execs:43334,op:splice,rep:4
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000035,sig:06,src:000009,time:5803,execs:43777,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000036,sig:06,src:000023+000143,time:5904,execs:44530,op:splice,rep:5
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000037,sig:06,src:000023+000143,time:5959,execs:44888,op:splice,rep:3
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000038,sig:06,src:000084,time:6653,execs:50083,op:havoc,rep:1
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000039,sig:06,src:000228,time:6776,execs:51037,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000040,sig:06,src:000208,time:6871,execs:51748,op:havoc,rep:3
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000041,sig:06,src:000000,time:7482,execs:56413,op:havoc,rep:1
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000042,sig:06,src:000000,time:7514,execs:56623,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000043,sig:06,src:000155,time:9540,execs:70176,op:havoc,rep:11
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000044,sig:06,src:000214,time:9767,execs:71865,op:havoc,rep:11
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000045,sig:06,src:000017+000239,time:13807,execs:102718,op:splice,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000046,sig:06,src:000125+000298,time:16887,execs:126602,op:splice,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000047,sig:06,src:000121+000138,time:19513,execs:146641,op:splice,rep:13
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000048,sig:06,src:000291,time:24565,execs:185374,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000049,sig:06,src:000317,time:26262,execs:198388,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000050,sig:06,src:000331,time:27323,execs:206754,op:havoc,rep:1
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000051,sig:06,src:000005+000247,time:78953,execs:593756,op:splice,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000052,sig:06,src:000090+000372,time:102543,execs:769682,op:splice,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000053,sig:06,src:000005+000247,time:108341,execs:813395,op:splice,rep:16
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000054,sig:06,src:000010+000133,time:125413,execs:943339,op:splice,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000055,sig:06,src:000002+000098,time:484230,execs:3686280,op:splice,rep:5
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000056,sig:06,src:000011,time:542917,execs:4136660,op:havoc,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000057,sig:06,src:000007+000209,time:600276,execs:4577225,op:splice,rep:21
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000058,sig:06,src:000007+000212,time:600277,execs:4577237,op:splice,rep:6
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000059,sig:06,src:000291+000414,time:615837,execs:4683378,op:splice,rep:2
zig-out/bin/decompress < tmp/flate-fuzz-decompress/id:000060,sig:06,src:000291+000414,time:615866,execs:4683423,op:splice,rep:4

error.CorruptInput
error.CorruptInput
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.EndOfStream
error.EndOfStream
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.EndOfStream
error.CorruptInput
OK len: 0
error.EndOfStream
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.EndOfStream
error.EndOfStream
error.EndOfStream
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.EndOfStream
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.CorruptInput
error.EndOfStream
error.EndOfStream
error.EndOfStream
  • Loading branch information
ianic committed Feb 9, 2024
1 parent bc91979 commit 5ca1454
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 14 deletions.
25 changes: 25 additions & 0 deletions bin/decompress.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const std = @import("std");
const flate = @import("flate");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() == .ok);
const allocator = gpa.allocator();

// Read the data from stdin
const stdin = std.io.getStdIn();
const data = try stdin.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(data);

// Try to parse the data
var fbs = std.io.fixedBufferStream(data);
const reader = fbs.reader();
var inflate = flate.raw.decompressor(reader);

const inflated = inflate.reader().readAllAlloc(allocator, std.math.maxInt(usize)) catch |err| {
std.debug.print("{}\n", .{err});
return;
};
defer allocator.free(inflated);
std.debug.print("OK len: {d}\n", .{inflated.len});
}
1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub fn build(b: *std.Build) void {
const binaries = [_]Binary{
.{ .name = "gzip", .src = "bin/gzip.zig" },
.{ .name = "gunzip", .src = "bin/gunzip.zig" },
.{ .name = "decompress", .src = "bin/decompress.zig" },
};
for (binaries) |i| {
const bin = b.addExecutable(.{
Expand Down
29 changes: 19 additions & 10 deletions src/CircularBuffer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const std = @import("std");
const assert = std.debug.assert;
const testing = std.testing;

const consts = @import("consts.zig").match;

const mask = 0xffff; // 64K - 1
const buffer_len = mask + 1; // 64K buffer

Expand All @@ -39,9 +41,14 @@ pub fn write(self: *Self, b: u8) void {

// Write match (back-reference to the same data slice) starting at `distance`
// back from current write position, and `length` of bytes.
pub fn writeMatch(self: *Self, length: u16, distance: u16) void {
pub fn writeMatch(self: *Self, length: u16, distance: u16) !void {
if (self.wp < distance or
length < consts.base_length or length > consts.max_length or
distance < consts.min_distance or distance > consts.max_distance)
{
return error.CorruptInput;
}
assert(self.wp - self.rp < mask);
assert(self.wp >= distance);

var from: usize = self.wp - distance;
const from_end: usize = from + length;
Expand Down Expand Up @@ -127,17 +134,17 @@ test "CircularBuffer copy" {
var sw: Self = .{};

sw.writeAll("a salad; ");
sw.writeMatch(5, 9);
sw.writeMatch(2, 3);
try sw.writeMatch(5, 9);
try sw.writeMatch(3, 3);

try testing.expectEqualStrings("a salad; a salsa", sw.read());
try testing.expectEqualStrings("a salad; a salsal", sw.read());
}

test "CircularBuffer copy overlap" {
var sw: Self = .{};

sw.writeAll("a b c ");
sw.writeMatch(8, 4);
try sw.writeMatch(8, 4);
sw.write('d');

try testing.expectEqualStrings("a b c b c b c d", sw.read());
Expand All @@ -147,7 +154,7 @@ test "CircularBuffer readAtMost" {
var sw: Self = .{};

sw.writeAll("0123456789");
sw.writeMatch(50, 10);
try sw.writeMatch(50, 10);

try testing.expectEqualStrings("0123456789" ** 6, sw.buffer[sw.rp..sw.wp]);
for (0..6) |i| {
Expand All @@ -167,7 +174,9 @@ test "CircularBuffer circular buffer" {
try testing.expectEqual(@as(usize, 1024), sw.wp);
try testing.expectEqual(@as(usize, 1024 * 63), sw.free());

sw.writeMatch(62 * 1024, 1024);
for (0..62 * 4) |_|
try sw.writeMatch(256, 1024); // write 62K

try testing.expectEqual(@as(usize, 0), sw.rp);
try testing.expectEqual(@as(usize, 63 * 1024), sw.wp);
try testing.expectEqual(@as(usize, 1024), sw.free());
Expand Down Expand Up @@ -215,11 +224,11 @@ test "CircularBuffer copy over border" {
sw.rp = sw.wp;

sw.writeAll("0123456789");
sw.writeMatch(15, 5);
try sw.writeMatch(15, 5);

try testing.expectEqualStrings("012345678956789", sw.read());
try testing.expectEqualStrings("5678956789", sw.read());

sw.writeMatch(20, 25);
try sw.writeMatch(20, 25);
try testing.expectEqualStrings("01234567895678956789", sw.read());
}
2 changes: 1 addition & 1 deletion src/deflate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ fn TokenDecoder(comptime WriterType: type) type {
for (tokens) |t| {
switch (t.kind) {
.literal => self.hist.write(t.literal()),
.match => self.hist.writeMatch(t.length(), t.distance()),
.match => try self.hist.writeMatch(t.length(), t.distance()),
}
if (self.hist.free() < 285) try self.flushWin();
}
Expand Down
1 change: 1 addition & 0 deletions src/huffman_decoder.zig
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ fn HuffmanDecoder(
const next_code = code + (@as(u16, 1) << (max_code_bits - sym.code_bits));
const next_idx = next_code >> lookup_shift;

if (next_idx > self.lookup.len or idx >= self.lookup.len) break;
if (sym.code_bits <= lookup_bits) {
// fill small lookup table
for (idx..next_idx) |j|
Expand Down
7 changes: 4 additions & 3 deletions src/inflate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub fn Inflate(comptime container: Container, comptime ReaderType: type) type {
try self.bits.fill(5 + 5 + 13);
const length = try self.decodeLength(code);
const distance = try self.decodeDistance(try self.bits.readF(u5, F.buffered));
self.hist.writeMatch(length, distance);
try self.hist.writeMatch(length, distance);
}

inline fn decodeLength(self: *Self, code: u8) !u16 {
Expand Down Expand Up @@ -179,7 +179,8 @@ pub fn Inflate(comptime container: Container, comptime ReaderType: type) type {
// lens slice starting at position pos. Returns number of positions
// advanced.
fn dynamicCodeLength(self: *Self, code: u16, lens: []u4, pos: usize) !usize {
assert(code <= 18);
if (pos >= lens.len or code > 18)
return error.CorruptInput;
switch (code) {
0...15 => {
// Represent code lengths of 0 - 15
Expand Down Expand Up @@ -219,7 +220,7 @@ pub fn Inflate(comptime container: Container, comptime ReaderType: type) type {
const length = try self.decodeLength(sym.symbol);
const dsm = try self.decodeSymbol(&self.dst_h);
const distance = try self.decodeDistance(dsm.symbol);
self.hist.writeMatch(length, distance);
try self.hist.writeMatch(length, distance);
},
.end_of_block => return true,
}
Expand Down

0 comments on commit 5ca1454

Please sign in to comment.