Skip to content

Commit

Permalink
feat(miniz_oxide) Return currently decompressed data on failure in de…
Browse files Browse the repository at this point in the history
…compress_to_vec.. functions
  • Loading branch information
oyvindln committed Aug 21, 2022
1 parent c08ac1c commit 8179633
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 26 deletions.
5 changes: 3 additions & 2 deletions miniz_oxide/src/inflate/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const HUFFLEN_TABLE: usize = 2;
pub mod inflate_flags {
/// Should we try to parse a zlib header?
///
/// If unset, [`decompress()`] will expect an RFC1951 deflate stream. If set, it will expect an
/// If unset, the function will expect an RFC1951 deflate stream. If set, it will expect a
/// RFC1950 zlib wrapper around the deflate stream.
pub const TINFL_FLAG_PARSE_ZLIB_HEADER: u32 = 1;

Expand Down Expand Up @@ -1456,7 +1456,8 @@ pub fn decompress(
// Mask the top bits since they may contain length info.
l.counter &= 511;

if l.counter == 256 {
if l.counter
== 256 {
// We hit the end of block symbol.
Action::Jump(BlockDone)
} else if l.counter > 285 {
Expand Down
67 changes: 51 additions & 16 deletions miniz_oxide/src/inflate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,43 @@ impl TINFLStatus {
}
}

/// Struct return when decompress_to_vec functions fail.
#[cfg(feature = "with-alloc")]
#[derive(Debug)]
pub struct DecompressError {
/// Decompressor status on failure. See [TINFLStatus] for details.
pub status: TINFLStatus,
/// The currently decompressed data if any.
pub output: Vec<u8>,
}

fn decompress_error(status: TINFLStatus, output: Vec<u8>) -> Result<Vec<u8>, DecompressError> {
Err(DecompressError { status, output })
}

/// Decompress the deflate-encoded data in `input` to a vector.
///
/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
/// NOTE: This function will not bound the output, so if the output is large enough it can result in an out of memory error.
/// It is therefore suggested to not use this for anything other than test programs, use the functions with a specified limit, or
/// ideally streaming decompression via the [flate2](https://github.com/alexcrichton/flate2-rs) library instead.
///
/// Returns a [`Result`] containing the [`Vec`] of decompressed data on success, and a [struct][DecompressError] containing the status and so far decompressed data if any on failure.
#[inline]
#[cfg(feature = "with-alloc")]
pub fn decompress_to_vec(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
pub fn decompress_to_vec(input: &[u8]) -> Result<Vec<u8>, DecompressError> {
decompress_to_vec_inner(input, 0, usize::max_value())
}

/// Decompress the deflate-encoded data (with a zlib wrapper) in `input` to a vector.
///
/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
/// NOTE: This function will not bound the output, so if the output is large enough it can result in an out of memory error.
/// It is therefore suggested to not use this for anything other than test programs, use the functions with a specified limit, or
/// ideally streaming decompression via the [flate2](https://github.com/alexcrichton/flate2-rs) library instead.
///
/// Returns a [`Result`] containing the [`Vec`] of decompressed data on success, and a [struct][DecompressError] containing the status and so far decompressed data if any on failure.
#[inline]
#[cfg(feature = "with-alloc")]
pub fn decompress_to_vec_zlib(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
pub fn decompress_to_vec_zlib(input: &[u8]) -> Result<Vec<u8>, DecompressError> {
decompress_to_vec_inner(
input,
inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER,
Expand All @@ -99,39 +121,49 @@ pub fn decompress_to_vec_zlib(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
}

/// Decompress the deflate-encoded data in `input` to a vector.
///
/// The vector is grown to at most `max_size` bytes; if the data does not fit in that size,
/// [`TINFLStatus::HasMoreOutput`] error is returned.
/// the error [struct][DecompressError] will contain the status [`TINFLStatus::HasMoreOutput`] and the data that was decompressed on failure.
///
/// As this function tries to decompress everything in one go, it's not ideal for general use outside of tests or where the output size is expected to be small.
/// It is suggested to use streaming decompression via the [flate2](https://github.com/alexcrichton/flate2-rs) library instead.
///
/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
/// Returns a [`Result`] containing the [`Vec`] of decompressed data on success, and a [struct][DecompressError] on failure.
#[inline]
#[cfg(feature = "with-alloc")]
pub fn decompress_to_vec_with_limit(input: &[u8], max_size: usize) -> Result<Vec<u8>, TINFLStatus> {
pub fn decompress_to_vec_with_limit(
input: &[u8],
max_size: usize,
) -> Result<Vec<u8>, DecompressError> {
decompress_to_vec_inner(input, 0, max_size)
}

/// Decompress the deflate-encoded data (with a zlib wrapper) in `input` to a vector.
/// The vector is grown to at most `max_size` bytes; if the data does not fit in that size,
/// [`TINFLStatus::HasMoreOutput`] error is returned.
/// the error [struct][DecompressError] will contain the status [`TINFLStatus::HasMoreOutput`] and the data that was decompressed on failure.
///
/// As this function tries to decompress everything in one go, it's not ideal for general use outside of tests or where the output size is expected to be small.
/// It is suggested to use streaming decompression via the [flate2](https://github.com/alexcrichton/flate2-rs) library instead.
///
/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
/// Returns a [`Result`] containing the [`Vec`] of decompressed data on success, and a [struct][DecompressError] on failure.
#[inline]
#[cfg(feature = "with-alloc")]
pub fn decompress_to_vec_zlib_with_limit(
input: &[u8],
max_size: usize,
) -> Result<Vec<u8>, TINFLStatus> {
) -> Result<Vec<u8>, DecompressError> {
decompress_to_vec_inner(input, inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER, max_size)
}

/// Backend of various to-[`Vec`] decompressions.
///
/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
/// Returns [`Vec`] of decompressed data on success and the [error struct][DecompressError] with details on failure.
#[cfg(feature = "with-alloc")]
fn decompress_to_vec_inner(
input: &[u8],
flags: u32,
max_output_size: usize,
) -> Result<Vec<u8>, TINFLStatus> {
) -> Result<Vec<u8>, DecompressError> {
let flags = flags | inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
let mut ret: Vec<u8> = vec![0; input.len().saturating_mul(2).min(max_output_size)];

Expand All @@ -156,14 +188,14 @@ fn decompress_to_vec_inner(
TINFLStatus::HasMoreOutput => {
// if the buffer has already reached the size limit, return an error
if ret.len() >= max_output_size {
return Err(TINFLStatus::HasMoreOutput);
return decompress_error(TINFLStatus::HasMoreOutput, ret);
}
// calculate the new length, capped at `max_output_size`
let new_len = ret.len().saturating_mul(2).min(max_output_size);
ret.resize(new_len, 0);
}

_ => return Err(status),
_ => return decompress_error(status, ret),
}
}
}
Expand Down Expand Up @@ -224,7 +256,7 @@ pub fn decompress_slice_iter_to_slice<'out, 'inp>(
mod test {
use super::{
decompress_slice_iter_to_slice, decompress_to_vec_zlib, decompress_to_vec_zlib_with_limit,
TINFLStatus,
DecompressError, TINFLStatus,
};
const ENCODED: [u8; 20] = [
120, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4, 19,
Expand All @@ -246,7 +278,10 @@ mod test {
fn fail_to_decompress_with_limit() {
let res = decompress_to_vec_zlib_with_limit(&ENCODED[..], 8);
match res {
Err(TINFLStatus::HasMoreOutput) => (), // expected result
Err(DecompressError {
status: TINFLStatus::HasMoreOutput,
..
}) => (), // expected result
_ => panic!("Decompression output size limit was not enforced"),
}
}
Expand Down
46 changes: 38 additions & 8 deletions miniz_oxide/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn inf_issue_14() {
let result = decompress_to_vec_zlib(data.as_slice());
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error, TINFLStatus::Failed);
assert_eq!(error.status, TINFLStatus::Failed);
}

/// Fuzzed file that causes panics (subtract-with-overflow in debug, out-of-bounds in release)
Expand Down Expand Up @@ -165,18 +165,48 @@ fn issue_119_inflate_with_exact_limit() {
use miniz_oxide::inflate::{decompress_to_vec_zlib, decompress_to_vec_zlib_with_limit};

let compressed_data = [
120, 156, 237, 217, 65, 17, 194, 0, 16, 192, 192, 122, 193, 94, 13, 240, 232, 128, 12, 28, 160, 2, 53, 53, 130, 139, 220, 227, 118, 21, 228, 159, 227, 13, 0, 212, 126, 211, 1, 0, 176, 208, 99, 58, 0, 0, 22, 122, 78, 7, 0, 192, 66, 223, 233, 0, 0, 88, 200, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208,
243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127,
1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 247, 116, 0, 0, 44, 116, 78, 7, 0, 192, 66, 215, 116, 0, 0, 44, 244, 154, 14, 0, 128, 133, 62, 211, 1, 0, 176, 144, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5,
128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 254, 53, 209, 27, 197
].as_slice();
120, 156, 237, 217, 65, 17, 194, 0, 16, 192, 192, 122, 193, 94, 13, 240, 232, 128, 12, 28,
160, 2, 53, 53, 130, 139, 220, 227, 118, 21, 228, 159, 227, 13, 0, 212, 126, 211, 1, 0,
176, 208, 99, 58, 0, 0, 22, 122, 78, 7, 0, 192, 66, 223, 233, 0, 0, 88, 200, 255, 5, 128,
158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208,
243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122,
254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207,
255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249,
191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255,
23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255,
2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0,
232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11,
0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1,
160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0,
244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5,
128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 247, 116, 0, 0, 44, 116, 78, 7, 0, 192, 66, 215,
116, 0, 0, 44, 244, 154, 14, 0, 128, 133, 62, 211, 1, 0, 176, 144, 255, 11, 0, 61, 255, 23,
0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2,
64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0,
232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11,
0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1,
160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0,
244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5,
128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0,
208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0,
122, 254, 47, 0, 244, 252, 95, 0, 232, 249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64,
207, 255, 5, 128, 158, 255, 11, 0, 61, 255, 23, 0, 122, 254, 47, 0, 244, 252, 95, 0, 232,
249, 191, 0, 208, 243, 127, 1, 160, 231, 255, 2, 64, 207, 255, 5, 128, 158, 255, 11, 0, 61,
255, 23, 0, 122, 254, 47, 0, 244, 254, 53, 209, 27, 197,
]
.as_slice();

let decompressed_size = decompress_to_vec_zlib(compressed_data)
.expect("test is not valid, data must correctly decompress when not limited")
.len();

let _ = decompress_to_vec_zlib_with_limit(compressed_data, decompressed_size)
.expect(format!("data decompression failed when limited to {}", decompressed_size).as_str());
let _ = decompress_to_vec_zlib_with_limit(compressed_data, decompressed_size).expect(
format!(
"data decompression failed when limited to {}",
decompressed_size
)
.as_str(),
);
}

/*
Expand Down

0 comments on commit 8179633

Please sign in to comment.