Skip to content

Commit

Permalink
fix(deflate): compress_to_vec infinite loop
Browse files Browse the repository at this point in the history
Fixed bug causing an infinite loop when compress_to_vec was called with empty or 1-length input.

Closes #75
  • Loading branch information
oyvindln committed Apr 11, 2020
1 parent 383b597 commit 8577636
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 5 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ panic = "abort"

[profile.release]
panic = "abort"
#overflow-checks = true

[badges]
travis-ci = { repository = "Frommi/miniz_oxide" }
8 changes: 4 additions & 4 deletions miniz_oxide/src/deflate/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1760,12 +1760,12 @@ fn compress_normal(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> boo
let dictb = &mut d.dict.b;

let mut dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
let mut ins_pos = lookahead_pos + lookahead_size - 2;
let mut ins_pos = (lookahead_pos + lookahead_size - 2) & LZ_DICT_SIZE_MASK;
let mut hash = (u32::from(dictb.dict[(ins_pos & LZ_DICT_SIZE_MASK) as usize])
<< LZ_HASH_SHIFT)
^ u32::from(dictb.dict[((ins_pos + 1) & LZ_DICT_SIZE_MASK) as usize]);

lookahead_size += num_bytes_to_process as u32;
lookahead_size = (lookahead_size + num_bytes_to_process as u32) & LZ_DICT_SIZE_MASK;
for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
dictb.dict[dst_pos as usize] = c;
if (dst_pos as usize) < MAX_MATCH_LEN - 1 {
Expand Down Expand Up @@ -1889,7 +1889,7 @@ fn compress_normal(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> boo
saved_match_len = cur_match_len;
}

lookahead_pos += len_to_move;
lookahead_pos = (lookahead_pos + len_to_move) & LZ_DICT_SIZE as u32;
assert!(lookahead_size >= len_to_move);
lookahead_size -= len_to_move;
d.dict.size = cmp::min(d.dict.size + len_to_move, LZ_DICT_SIZE as u32);
Expand Down Expand Up @@ -2051,7 +2051,7 @@ fn compress_fast(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> bool

d.lz.consume_flag();
d.lz.total_bytes += cur_match_len;
lookahead_pos += cur_match_len;
lookahead_pos = (lookahead_pos + cur_match_len) & LZ_DICT_SIZE_MASK;
d.dict.size = cmp::min(d.dict.size + cur_match_len, LZ_DICT_SIZE as u32);
cur_pos = (cur_pos + cur_match_len) & LZ_DICT_SIZE_MASK;
lookahead_size -= cur_match_len;
Expand Down
3 changes: 2 additions & 1 deletion miniz_oxide/src/deflate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ fn compress_to_vec_inner(input: &[u8], level: u8, window_bits: i32, strategy: i3
// The comp flags function sets the zlib flag if the window_bits parameter is > 0.
let flags = create_comp_flags_from_zip_params(level.into(), window_bits, strategy);
let mut compressor = CompressorOxide::new(flags);
let mut output = vec![0; input.len() / 2];
// Output vec, need to make sure it's not empty to avoid an infinite loop.
let mut output = vec![0; std::cmp::max(input.len() / 2, 2)];

let mut in_pos = 0;
let mut out_pos = 0;
Expand Down
22 changes: 22 additions & 0 deletions miniz_oxide/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ fn roundtrip_lvl_0() {
roundtrip(0);
}

#[test]
fn large_file() {
let data = get_test_file_data("large_file/lf");
let enc = compress_to_vec(&data.as_slice()[..], 3);

let dec = decompress_to_vec(enc.as_slice()).unwrap();
assert!(data == dec);
}

#[test]
fn zlib_header_level() {
let level = 6;
Expand Down Expand Up @@ -148,3 +157,16 @@ fn need_more_input_has_more_output_at_same_time() {
decomp(&input[..11727]); // Fail: NeedsMoreInput even if the output buffer is also full!
decomp(&input[..11728]); // Fail: NeedsMoreInput even if the output buffer is also full!
}

#[test]
fn issue_75_empty_input_infinite_loop() {
// Make sure compression works with empty input,
// a bug resulted in this causing an infinite loop in
// compress_to_vec_inner.
let c = miniz_oxide::deflate::compress_to_vec(&[], 6);
let d = miniz_oxide::inflate::decompress_to_vec(&c).expect("decompression failed!");
assert_eq!(d.len(), 0);
let c = miniz_oxide::deflate::compress_to_vec(&[0], 6);
let d = miniz_oxide::inflate::decompress_to_vec(&c).expect("decompression failed!");
assert!(&d == &[0]);
}

0 comments on commit 8577636

Please sign in to comment.