Skip to content
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
25 changes: 14 additions & 11 deletions fuzz/fuzz_targets/compress_then_decompress_chunked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,16 @@ use libfuzzer_sys::fuzz_target;

/// compress the data with the stock C bzip2
fn compress_c(data: &[u8], compression_level: u8, work_factor: u8) -> Vec<u8> {

// output buffer for compression, will get resized later if needed
let mut output = vec![0u8; 1024];
let mut output = Vec::<u8>::with_capacity(1024);

let mut stream = libbz2_rs_sys::bz_stream {
next_in: data.as_ptr() as *mut _,
avail_in: data.len() as _,
total_in_lo32: 0,
total_in_hi32: 0,
avail_out: output.capacity() as _,
next_out: output.as_mut_ptr() as *mut _,
avail_out: output.len() as _,
total_out_lo32: 0,
total_out_hi32: 0,
state: std::ptr::null_mut(),
Expand All @@ -46,10 +45,14 @@ fn compress_c(data: &[u8], compression_level: u8, work_factor: u8) -> Vec<u8> {
let error = loop {
match unsafe { libbz2_rs_sys::BZ2_bzCompress(&mut stream, BZ_FINISH) } {
BZ_FINISH_OK => {
let used = output.len() - stream.avail_out as usize;
let used = output.capacity() - stream.avail_out as usize;

// Safety: we've written this many (initialized!) bytes to the output.
unsafe { output.set_len(used) };

// The output buffer is full, resize it
let add_space: u32 = Ord::max(1024, output.len().try_into().unwrap());
output.resize(output.len() + add_space as usize, 0);
let add_space: u32 = Ord::max(1024, output.capacity().try_into().unwrap());
output.reserve(add_space as usize);

// If resize() reallocates, it may have moved in memory
stream.next_out = output.as_mut_ptr().cast::<i8>().wrapping_add(used);
Expand All @@ -70,11 +73,11 @@ fn compress_c(data: &[u8], compression_level: u8, work_factor: u8) -> Vec<u8> {
assert_eq!(error, BZ_OK);

// truncate the output buffer down to the actual number of compressed bytes
output.truncate(
((u64::from(stream.total_out_hi32) << 32) + u64::from(stream.total_out_lo32))
.try_into()
.unwrap(),
);
let total = u64::from(stream.total_out_hi32) << 32 | u64::from(stream.total_out_lo32);
unsafe { output.set_len(usize::try_from(total).unwrap()) };

// Just check that this byte is in fact initialized.
std::hint::black_box(output.last() == Some(&0));

unsafe {
// cleanup, should always succeed
Expand Down
27 changes: 20 additions & 7 deletions fuzz/fuzz_targets/decompress_chunked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ fuzz_target!(|fuzz_data: &[u8]| -> Corpus {
Corpus::Keep
};

// initialize output buffer, resizing will happen later if needed
// using the input length is just a quick heuristic and not otherwise meaningful or necessary
let mut output = vec![0u8; fuzz_data.len()];
let output_len = output.len() as _;
// Initialize output buffer, resizing will happen later if needed
// using the input length is just a quick heuristic and not otherwise meaningful or necessary.
//
// Deliberately use uninitialized memory for the output, to ensure we never
// read/use uninitialized data.
let mut output = Vec::<u8>::with_capacity(fuzz_data.len());
let output_len = output.capacity() as _;

// set output buffer in decompression context
stream.next_out = output.as_mut_ptr().cast::<core::ffi::c_char>();
Expand All @@ -73,10 +76,14 @@ fuzz_target!(|fuzz_data: &[u8]| -> Corpus {
// there is still input from this chunk left to process but no more output buffer
// this means we have to increase the output buffer and retry the decompress
if stream.avail_in > 0 && stream.avail_out == 0 {
let used = output.len() - stream.avail_out as usize;
let used = output.capacity() - stream.avail_out as usize;

// Safety: we've written this many (initialized!) bytes to the output.
unsafe { output.set_len(used) };

// The dest buffer is full, resize it
let add_space: u32 = Ord::max(4096, output.len().try_into().unwrap());
output.resize(output.len() + add_space as usize, 0);
let add_space: u32 = Ord::max(4096, output.capacity().try_into().unwrap());
output.reserve(add_space as usize);

// If resize() reallocates, it may have moved in memory
stream.next_out = output.as_mut_ptr().cast::<i8>().wrapping_add(used);
Expand Down Expand Up @@ -108,6 +115,12 @@ fuzz_target!(|fuzz_data: &[u8]| -> Corpus {
}
}

let total = u64::from(stream.total_out_hi32) << 32 | u64::from(stream.total_out_lo32);
unsafe { output.set_len(usize::try_from(total).unwrap()) };

// Just check that this byte is in fact initialized.
std::hint::black_box(output.last() == Some(&0));

unsafe {
// clean up state, should always succeed
let err = BZ2_bzDecompressEnd(&mut stream);
Expand Down
44 changes: 24 additions & 20 deletions test-libbz2-rs-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ pub unsafe fn decompress_c_with_capacity(
opaque: std::ptr::null_mut::<libc::c_void>(),
};

let mut dest = vec![0u8; capacity];
// Deliberately use uninitialized memory for the output.
let mut dest = Vec::<u8>::with_capacity(capacity);

strm.bzalloc = None;
strm.bzfree = None;
Expand All @@ -271,7 +272,7 @@ pub unsafe fn decompress_c_with_capacity(
return (ret, vec![]);
}
strm.avail_in = source_len;
strm.avail_out = dest.len() as _;
strm.avail_out = dest.capacity() as _;
strm.next_in = source as *mut libc::c_char;
strm.next_out = dest.as_mut_ptr().cast::<core::ffi::c_char>();

Expand All @@ -282,10 +283,14 @@ pub unsafe fn decompress_c_with_capacity(
BZ2_bzDecompressEnd(&mut strm);
break BZ_UNEXPECTED_EOF;
} else {
let used = dest.len() - strm.avail_out as usize;
let used = dest.capacity() - strm.avail_out as usize;

// We've written this many (initialized!) bytes to the output.
dest.set_len(used);

// The dest buffer is full.
let add_space: u32 = Ord::max(1024, dest.len().try_into().unwrap());
dest.resize(dest.len() + add_space as usize, 0);
let add_space: u32 = Ord::max(1024, dest.capacity().try_into().unwrap());
dest.reserve(add_space as usize);

// If resize() reallocates, it may have moved in memory.
strm.next_out = dest.as_mut_ptr().cast::<i8>().wrapping_add(used);
Expand All @@ -305,11 +310,8 @@ pub unsafe fn decompress_c_with_capacity(
}
};

dest.truncate(
((u64::from(strm.total_out_hi32) << 32) + u64::from(strm.total_out_lo32))
.try_into()
.unwrap(),
);
let total = (u64::from(strm.total_out_hi32) << 32) + u64::from(strm.total_out_lo32);
dest.set_len(usize::try_from(total).unwrap());

(ret, dest)
}
Expand Down Expand Up @@ -341,7 +343,8 @@ pub unsafe fn decompress_rs_with_capacity(
opaque: std::ptr::null_mut::<libc::c_void>(),
};

let mut dest = vec![0u8; capacity];
// Deliberately use uninitialized memory for the output.
let mut dest = Vec::<u8>::with_capacity(capacity);

strm.bzalloc = None;
strm.bzfree = None;
Expand All @@ -352,7 +355,7 @@ pub unsafe fn decompress_rs_with_capacity(
return (ret, vec![]);
}
strm.avail_in = source_len;
strm.avail_out = dest.len() as _;
strm.avail_out = dest.capacity() as _;
strm.next_in = source as *mut libc::c_char;
strm.next_out = dest.as_mut_ptr().cast::<core::ffi::c_char>();

Expand All @@ -363,10 +366,14 @@ pub unsafe fn decompress_rs_with_capacity(
BZ2_bzDecompressEnd(&mut strm);
break BZ_UNEXPECTED_EOF;
} else {
let used = dest.len() - strm.avail_out as usize;
let used = dest.capacity() - strm.avail_out as usize;

// We've written this many (initialized!) bytes to the output.
dest.set_len(used);

// The dest buffer is full.
let add_space: u32 = Ord::max(1024, dest.len().try_into().unwrap());
dest.resize(dest.len() + add_space as usize, 0);
let add_space: u32 = Ord::max(1024, dest.capacity().try_into().unwrap());
dest.reserve(add_space as usize);

// If resize() reallocates, it may have moved in memory.
strm.next_out = dest.as_mut_ptr().cast::<i8>().wrapping_add(used);
Expand All @@ -386,11 +393,8 @@ pub unsafe fn decompress_rs_with_capacity(
}
};

dest.truncate(
((u64::from(strm.total_out_hi32) << 32) + u64::from(strm.total_out_lo32))
.try_into()
.unwrap(),
);
let total = (u64::from(strm.total_out_hi32) << 32) + u64::from(strm.total_out_lo32);
dest.set_len(usize::try_from(total).unwrap());

(ret, dest)
}
Expand Down
Loading