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

Loop plus match #248

Merged
merged 10 commits into from
Nov 14, 2024
216 changes: 216 additions & 0 deletions test-libz-rs-sys/src/inflate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,185 @@ fn uncompress_edge_cases() {
assert!(result.is_empty());
}

#[test]
fn gzip_header_fields_insufficient_space() {
let chunk_size = 16;

let input = b"Hello World\n";

let extra =
"Scheduling and executing async tasks is a job handled by an async runtime, such as\0";
let name =
"tokio, async-std, and smol. You've probably used them at some point, either directly or\0";
let comment =
"indirectly. They, along with many frameworks that require async, do their best to hide\0";

let config = DeflateConfig {
window_bits: 31,
..Default::default()
};

let mut stream = MaybeUninit::<libz_rs_sys::z_stream>::zeroed();

const VERSION: *const c_char = libz_rs_sys::zlibVersion();
const STREAM_SIZE: c_int = core::mem::size_of::<libz_rs_sys::z_stream>() as c_int;

let err = unsafe {
libz_rs_sys::deflateInit2_(
stream.as_mut_ptr(),
config.level,
config.method as i32,
config.window_bits,
config.mem_level,
config.strategy as i32,
VERSION,
STREAM_SIZE,
)
};
assert_eq!(err, 0);

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

let mut header = libz_rs_sys::gz_header {
text: 0,
time: 0,
xflags: 0,
os: 0,
extra: extra.as_ptr() as *mut _,
extra_len: extra.len() as _,
extra_max: 0,
name: name.as_ptr() as *mut _,
name_max: 0,
comment: comment.as_ptr() as *mut _,
comm_max: 0,
hcrc: 1,
done: 0,
};

let err = unsafe { libz_rs_sys::deflateSetHeader(stream, &mut header) };
assert_eq!(err, 0);

let mut output_rs = [0u8; 512];
stream.next_out = output_rs.as_mut_ptr();
stream.avail_out = output_rs.len() as _;

for chunk in input.chunks(chunk_size) {
stream.next_in = chunk.as_ptr() as *mut u8;
stream.avail_in = chunk.len() as _;

let err = unsafe { deflate(stream, InflateFlush::NoFlush as _) };

assert_eq!(err, ReturnCode::Ok as i32, "{:?}", stream.msg);
}

let err = unsafe { libz_rs_sys::deflate(stream, InflateFlush::Finish as _) };
assert_eq!(ReturnCode::from(err), ReturnCode::StreamEnd);

let output_rs = &mut output_rs[..stream.total_out as usize];

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

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

const VERSION: *const c_char = libz_rs_sys::zlibVersion();
const STREAM_SIZE: c_int = core::mem::size_of::<z_stream>() as c_int;

let err = unsafe {
inflateInit2_(
stream.as_mut_ptr(),
config.window_bits,
VERSION,
STREAM_SIZE,
)
};
assert_eq!(err, 0);

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

let mut output = [0u8; 64];
stream.next_out = output.as_mut_ptr();
stream.avail_out = output.len() as _;

let mut extra_buf = [0u8; 64];
assert!(extra_buf.len() < extra.len());
let mut name_buf = [0u8; 1];
assert!(name_buf.len() < name.len());
let mut comment_buf = [0u8; 64];
assert!(comment_buf.len() < comment.len());

let mut header = gz_header {
text: 0,
time: 0,
xflags: 0,
os: 0,
extra: extra_buf.as_mut_ptr(),
extra_len: 0,
extra_max: extra_buf.len() as _,
name: name_buf.as_mut_ptr(),
name_max: name_buf.len() as _,
comment: comment_buf.as_mut_ptr(),
comm_max: comment_buf.len() as _,
hcrc: 0,
done: 0,
};

let err = unsafe { inflateGetHeader(stream, &mut header) };
assert_eq!(err, 0);

for chunk in output_rs.chunks_mut(chunk_size) {
stream.next_in = chunk.as_mut_ptr();
stream.avail_in = chunk.len() as _;

let err = unsafe { inflate(stream, InflateFlush::NoFlush as _) };

if err == ReturnCode::StreamEnd as i32 {
break;
}

assert_eq!(err, ReturnCode::Ok as i32);
}

let err = unsafe { inflateEnd(stream) };
assert_eq!(err, ReturnCode::Ok as i32);

assert!(!header.extra.is_null());
assert_eq!(
if extra_buf.last() != Some(&0) {
std::str::from_utf8(&extra_buf).unwrap()
} else {
CStr::from_bytes_until_nul(&extra_buf)
.unwrap()
.to_str()
.unwrap()
},
&extra[..extra_buf.len()]
);

assert!(!header.name.is_null());
assert_eq!(
if name_buf.last() != Some(&0) {
std::str::from_utf8(&name_buf).unwrap()
} else {
CStr::from_bytes_until_nul(&name_buf)
.unwrap()
.to_str()
.unwrap()
},
&name[..name_buf.len()]
);

assert!(!header.comment.is_null());
assert_eq!(
std::str::from_utf8(&comment_buf).unwrap(),
&comment.trim_end_matches('\0')[..comment_buf.len()]
);

(extra_buf, name_buf, comment_buf)
});
}

#[test]
fn gzip_chunked_1_byte() {
gzip_chunked(1);
Expand Down Expand Up @@ -1969,3 +2148,40 @@ fn issue_232() {

unsafe { inflateEnd(&mut stream) };
}

#[test]
fn blow_up_the_stack_1() {
// requires a sequence of states that would blow up the stack if inflate is not stack safe.

const INPUT: &[u8] = include_bytes!("test-data/blow_up_the_stack_1.gz");

let mut output_ng = vec![0; INPUT.len() * 128];
let mut output_rs = vec![0; INPUT.len() * 128];

let config = InflateConfig::default();

let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, INPUT, config);
assert_eq!(err, ReturnCode::DataError);

let (_, err) = uncompress_slice(&mut output_rs, INPUT, config);
assert_eq!(err, ReturnCode::DataError);
}

#[test]
#[cfg_attr(miri, ignore = "slow")]
fn blow_up_the_stack_2() {
// requires a sequence of states that would blow up the stack if inflate is not stack safe.

const INPUT: &[u8] = include_bytes!("test-data/blow_up_the_stack_2.gz");

let mut output_ng = vec![0; INPUT.len() * 128];
let mut output_rs = vec![0; INPUT.len() * 128];

let config = InflateConfig::default();

let (_, err) = crate::helpers::uncompress_slice_ng(&mut output_ng, INPUT, config);
assert_eq!(err, ReturnCode::DataError);

let (_, err) = uncompress_slice(&mut output_rs, INPUT, config);
assert_eq!(err, ReturnCode::DataError);
}
Binary file not shown.
Binary file not shown.
Loading
Loading