diff --git a/miniz_oxide/src/inflate/core.rs b/miniz_oxide/src/inflate/core.rs index 38bdacbb..630e5e6f 100644 --- a/miniz_oxide/src/inflate/core.rs +++ b/miniz_oxide/src/inflate/core.rs @@ -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; @@ -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 { diff --git a/miniz_oxide/src/inflate/mod.rs b/miniz_oxide/src/inflate/mod.rs index f17817c3..064429ad 100644 --- a/miniz_oxide/src/inflate/mod.rs +++ b/miniz_oxide/src/inflate/mod.rs @@ -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, +} + +fn decompress_error(status: TINFLStatus, output: Vec) -> Result, 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, TINFLStatus> { +pub fn decompress_to_vec(input: &[u8]) -> Result, 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, TINFLStatus> { +pub fn decompress_to_vec_zlib(input: &[u8]) -> Result, DecompressError> { decompress_to_vec_inner( input, inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER, @@ -99,39 +121,49 @@ pub fn decompress_to_vec_zlib(input: &[u8]) -> Result, 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, TINFLStatus> { +pub fn decompress_to_vec_with_limit( + input: &[u8], + max_size: usize, +) -> Result, 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, TINFLStatus> { +) -> Result, 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, TINFLStatus> { +) -> Result, DecompressError> { let flags = flags | inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; let mut ret: Vec = vec![0; input.len().saturating_mul(2).min(max_output_size)]; @@ -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), } } } @@ -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, @@ -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"), } } diff --git a/miniz_oxide/tests/test.rs b/miniz_oxide/tests/test.rs index 388d5d8e..e689fb52 100644 --- a/miniz_oxide/tests/test.rs +++ b/miniz_oxide/tests/test.rs @@ -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) @@ -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(), + ); } /*