Skip to content

Commit f02b9df

Browse files
committed
add inflateValidate
1 parent 5c62764 commit f02b9df

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

libz-rs-sys/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,25 @@ pub unsafe extern "C-unwind" fn inflateUndermine(strm: *mut z_stream, subvert: i
10651065
}
10661066
}
10671067

1068+
#[doc(hidden)]
1069+
/// # Safety
1070+
///
1071+
/// The caller must guarantee that
1072+
///
1073+
/// * Either
1074+
/// - `strm` is `NULL`
1075+
/// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`inflateInit_`] or similar
1076+
#[cfg_attr(feature = "export-symbols", export_name = prefix!(inflateValidate))]
1077+
pub unsafe extern "C" fn inflateValidate(strm: *mut z_stream, check: i32) -> c_int {
1078+
let Some(stream) = InflateStream::from_stream_mut(strm) else {
1079+
return ReturnCode::StreamError as _;
1080+
};
1081+
1082+
zlib_rs::inflate::validate(stream, check != 0);
1083+
1084+
ReturnCode::Ok as _
1085+
}
1086+
10681087
#[doc(hidden)]
10691088
/// ## Safety
10701089
///

test-libz-rs-sys/src/inflate.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,3 +2867,162 @@ fn inflate_copy_after_half_input() {
28672867
out[..total].to_vec()
28682868
});
28692869
}
2870+
2871+
#[test]
2872+
fn inflate_validate_toggles_checksum_update() {
2873+
let input = include_bytes!("test-data/compression-corpus/The fastest WASM zlib.md.gzip-9.gz");
2874+
2875+
// Check that `inflateValidate` toggles checksum updating.
2876+
//
2877+
// - decompress normally, the checksum should update
2878+
// - toggle, the checksum should not update
2879+
// - toggle again, the checksum should update
2880+
assert_eq_rs_ng!({
2881+
let mut stream = MaybeUninit::<z_stream>::zeroed();
2882+
let ret = inflateInit2_(
2883+
stream.as_mut_ptr(),
2884+
16 + 15, // gzip + max window
2885+
zlibVersion(),
2886+
core::mem::size_of::<z_stream>() as c_int,
2887+
);
2888+
assert_eq!(ret, Z_OK);
2889+
2890+
let stream = stream.assume_init_mut();
2891+
2892+
let mut out1 = vec![0u8; 16 * 1024];
2893+
let mut out2 = vec![0u8; 16 * 1024];
2894+
let mut out3 = vec![0u8; 16 * 1024];
2895+
2896+
let check1 = {
2897+
let ret = inflateValidate(stream, 1);
2898+
assert_eq!(ret, Z_OK);
2899+
2900+
stream.next_in = input.as_ptr() as *mut _;
2901+
stream.avail_in = input.len() as _;
2902+
stream.next_out = out1.as_mut_ptr();
2903+
stream.avail_out = out1.len() as _;
2904+
2905+
let ret = loop {
2906+
let ret = inflate(stream, InflateFlush::NoFlush as _);
2907+
2908+
assert!(
2909+
matches!(ret, Z_OK | Z_BUF_ERROR | Z_STREAM_END),
2910+
"unexpected inflate return (run 1): {}",
2911+
ret
2912+
);
2913+
2914+
if matches!(ret, Z_STREAM_END) {
2915+
break ret;
2916+
}
2917+
2918+
if stream.avail_in == 0 {
2919+
break ret;
2920+
}
2921+
2922+
if stream.avail_out == 0 {
2923+
unreachable!("run 1: not enough output space");
2924+
}
2925+
};
2926+
2927+
assert_eq!(ret, Z_STREAM_END);
2928+
out1.truncate(stream.total_out as usize);
2929+
stream.adler
2930+
};
2931+
2932+
assert_eq!(inflateReset(stream), Z_OK);
2933+
2934+
let check2 = {
2935+
let ret = inflateValidate(stream, 0);
2936+
assert_eq!(ret, Z_OK);
2937+
2938+
stream.next_in = input.as_ptr() as *mut _;
2939+
stream.avail_in = input.len() as _;
2940+
stream.next_out = out2.as_mut_ptr();
2941+
stream.avail_out = out2.len() as _;
2942+
2943+
let ret = loop {
2944+
let ret = inflate(stream, InflateFlush::NoFlush as _);
2945+
2946+
assert!(
2947+
matches!(ret, Z_OK | Z_BUF_ERROR | Z_STREAM_END),
2948+
"unexpected inflate return (run 2): {}",
2949+
ret
2950+
);
2951+
2952+
if matches!(ret, Z_STREAM_END) {
2953+
break ret;
2954+
}
2955+
2956+
if stream.avail_in == 0 {
2957+
break ret;
2958+
}
2959+
2960+
if stream.avail_out == 0 {
2961+
unreachable!("run 2: not enough output space");
2962+
}
2963+
};
2964+
2965+
assert_eq!(ret, Z_STREAM_END);
2966+
out2.truncate(stream.total_out as usize);
2967+
stream.adler
2968+
};
2969+
2970+
// Output must be identical, regardless of validation.
2971+
assert_eq!(out1, out2);
2972+
2973+
assert_ne!(
2974+
check1, check2,
2975+
"checksum with validation disabled unexpectedly matches the validated checksum",
2976+
);
2977+
2978+
assert_eq!(inflateReset(stream), Z_OK);
2979+
2980+
let check3 = {
2981+
let ret = inflateValidate(stream, 0);
2982+
assert_eq!(ret, Z_OK);
2983+
let ret = inflateValidate(stream, 1);
2984+
assert_eq!(ret, Z_OK);
2985+
2986+
stream.next_in = input.as_ptr() as *mut _;
2987+
stream.avail_in = input.len() as _;
2988+
stream.next_out = out3.as_mut_ptr();
2989+
stream.avail_out = out3.len() as _;
2990+
2991+
let ret = loop {
2992+
let ret = inflate(stream, InflateFlush::NoFlush as _);
2993+
2994+
assert!(
2995+
matches!(ret, Z_OK | Z_BUF_ERROR | Z_STREAM_END),
2996+
"unexpected inflate return (run 3): {}",
2997+
ret
2998+
);
2999+
3000+
if matches!(ret, Z_STREAM_END) {
3001+
break ret;
3002+
}
3003+
3004+
if stream.avail_in == 0 {
3005+
break ret;
3006+
}
3007+
3008+
if stream.avail_out == 0 {
3009+
unreachable!("run 3: not enough output space");
3010+
}
3011+
};
3012+
3013+
assert_eq!(ret, Z_STREAM_END);
3014+
out3.truncate(stream.total_out as usize);
3015+
stream.adler
3016+
};
3017+
3018+
assert_eq!(out1, out3);
3019+
3020+
// With validation back on, checksum should again match the first run
3021+
assert_eq!(
3022+
check1, check3,
3023+
"checksum with validation re-enabled does not match validated run"
3024+
);
3025+
3026+
assert_eq!(inflateEnd(stream), Z_OK);
3027+
});
3028+
}

test-libz-rs-sys/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ macro_rules! assert_eq_rs_ng {
2828
#[allow(unused)]
2929
fn inflateCodesUsed(strm: *mut z_stream) -> core::ffi::c_ulong;
3030

31+
#[allow(unused)]
32+
fn inflateValidate(strm: *mut z_stream, check: i32) -> core::ffi::c_int;
33+
3134
#[allow(unused)]
3235
fn deflateGetDictionary(
3336
strm: *const z_stream,

zlib-rs/src/inflate.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,6 +2566,17 @@ pub fn undermine(stream: &mut InflateStream, subvert: i32) -> ReturnCode {
25662566
ReturnCode::Ok
25672567
}
25682568

2569+
/// Configures whether the checksum is calculated and checked.
2570+
pub fn validate(stream: &mut InflateStream, check: bool) -> ReturnCode {
2571+
if check && stream.state.wrap != 0 {
2572+
stream.state.wrap |= 0b100;
2573+
} else {
2574+
stream.state.wrap &= !0b100;
2575+
}
2576+
2577+
ReturnCode::Ok
2578+
}
2579+
25692580
pub fn mark(stream: &InflateStream) -> c_long {
25702581
if stream.next_out.is_null() || (stream.next_in.is_null() && stream.avail_in != 0) {
25712582
return c_long::MIN;

0 commit comments

Comments
 (0)