Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix panic in crc32 hash calc #222

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions test-libz-rs-sys/src/deflate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2323,5 +2323,124 @@ fn copy_uninitialized_window_section() {

let err = unsafe { deflateEnd(stream) };
assert_eq!(ReturnCode::from(err), ReturnCode::Ok);

dest
});
}

#[test]
fn crc32_hash_calc_uninitialized_memory() {
// by default the `Crc32HashCalc::insert_string` function tries to read some uninitialized
// bytes in this case. That panicked before, and we now handle it properly.
//
// see https://github.com/trifectatechfoundation/zlib-rs/issues/219

let mut source = [
96, 48, 113, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 7,
0, 0, 0, 64, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 86, 86, 86, 86, 86, 86, 86,
86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
86, 86, 0, 0, 0, 0, 121, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 31, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 121, 31, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 121, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];

let config = DeflateConfig {
level: 0,
method: Method::Deflated,
window_bits: 31,
mem_level: 3,
strategy: Strategy::Default,
};

assert_eq_rs_ng!({
let mut stream = MaybeUninit::zeroed();

let err = unsafe {
deflateInit2_(
stream.as_mut_ptr(),
config.level,
config.method as i32,
config.window_bits,
config.mem_level,
config.strategy as i32,
zlibVersion(),
core::mem::size_of::<z_stream>() as c_int,
)
};
assert_eq!(ReturnCode::from(err), ReturnCode::Ok);

let stream = unsafe { stream.assume_init_mut() };

let buf_size = unsafe { deflateBound(stream, source.len() as _) };

let mut dest = vec![0; buf_size as usize];
let chunk = 2u32;
let flush = DeflateFlush::NoFlush;

stream.next_in = source.as_mut_ptr().cast();
stream.avail_in = chunk; // First chunk.
stream.next_out = dest.as_mut_ptr().cast();
stream.avail_out = dest.len().try_into().unwrap();

// Deflate first chunk.
let err = unsafe { deflate(stream, flush as i32) };
assert_eq!(ReturnCode::from(err), ReturnCode::Ok);

// Change the parameters.
let new_level = 4;
let err = unsafe { deflateParams(stream, new_level, config.strategy as _) };
match ReturnCode::from(err) {
ReturnCode::Ok => {}
ReturnCode::BufError => {
// Flushing the current pending data may run us out of buffer space.
// Worst case double the buffer size.
let add_space = Ord::min(chunk, buf_size as u32);
dest.resize(dest.len() + add_space as usize, 0);

// If extend() reallocates, it may have moved in memory.
stream.next_out = dest.as_mut_ptr();
stream.avail_out += add_space;

let err = unsafe { deflateParams(stream, new_level, config.strategy as _) };
assert_eq!(ReturnCode::from(err), ReturnCode::Ok);
}
err => panic!("fatal {:?}", err),
}

// Deflate the rest in chunks.
let mut left: u32 = source.len() as u32 - chunk;
while left > 0 {
// Write the chunk.
let avail = Ord::min(chunk, left);
stream.avail_in = avail;
let err = unsafe { deflate(stream, flush as i32) };
match ReturnCode::from(err) {
ReturnCode::Ok => {
left -= avail;
}
ReturnCode::BufError => {
// Worst case double the buffer size.
let add_space = Ord::min(chunk, buf_size as u32);
dest.resize(dest.len() + add_space as usize, 0);

// If extend() reallocates, it may have moved in memory.
stream.next_out = dest.as_mut_ptr();
stream.avail_out += add_space;

left -= avail - stream.avail_in;
}
err => panic!("fatal {:?}", err),
}
}

assert_eq!(left, 0);

dest
});
}
10 changes: 8 additions & 2 deletions zlib-rs/src/deflate/hash_calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ impl StandardHashCalc {
pub fn insert_string(state: &mut State, string: usize, count: usize) {
let slice = &state.window.filled()[string + Self::HASH_CALC_OFFSET..];

// it can happen that insufficient bytes are initialized
// .take(count) generates worse assembly
for (i, w) in slice[..count + 3].windows(4).enumerate() {
let slice = &slice[..Ord::min(slice.len(), count + 3)];

for (i, w) in slice.windows(4).enumerate() {
let idx = string as u16 + i as u16;

let val = u32::from_le_bytes(w.try_into().unwrap());
Expand Down Expand Up @@ -205,8 +208,11 @@ impl Crc32HashCalc {
pub unsafe fn insert_string(state: &mut State, string: usize, count: usize) {
let slice = &state.window.filled()[string + Self::HASH_CALC_OFFSET..];

// it can happen that insufficient bytes are initialized
// .take(count) generates worse assembly
for (i, w) in slice[..count + 3].windows(4).enumerate() {
let slice = &slice[..Ord::min(slice.len(), count + 3)];

for (i, w) in slice.windows(4).enumerate() {
let idx = string as u16 + i as u16;

let val = u32::from_le_bytes(w.try_into().unwrap());
Expand Down
Loading