diff --git a/src/c_export.rs b/src/c_export.rs index 1bee699..44402de 100644 --- a/src/c_export.rs +++ b/src/c_export.rs @@ -231,15 +231,23 @@ impl<'io, ST: StateType> StreamOxide<'io, ST> { return Err(MZError::Param); } - let in_slice = stream - .next_in - .as_ref() - .map(|ptr| slice::from_raw_parts(ptr, stream.avail_in as usize)); - - let out_slice = stream - .next_out - .as_mut() - .map(|ptr| slice::from_raw_parts_mut(ptr, stream.avail_out as usize)); + let in_slice = if stream.next_in.is_null() { + None + } else { + Some(slice::from_raw_parts( + stream.next_in, + stream.avail_in as usize, + )) + }; + + let out_slice = if stream.next_out.is_null() { + None + } else { + Some(slice::from_raw_parts_mut( + stream.next_out, + stream.avail_out as usize, + )) + }; Ok(StreamOxide { next_in: in_slice, @@ -282,10 +290,12 @@ unmangle!( /// /// Returns MZ_ADLER32_INIT if ptr is `ptr::null`. pub unsafe extern "C" fn mz_adler32(adler: c_ulong, ptr: *const u8, buf_len: usize) -> c_ulong { - ptr.as_ref().map_or(MZ_ADLER32_INIT as c_ulong, |r| { - let data = slice::from_raw_parts(r, buf_len); + if ptr.is_null() { + MZ_ADLER32_INIT as c_ulong + } else { + let data = slice::from_raw_parts(ptr, buf_len); mz_adler32_oxide(adler as u32, data) as c_ulong - }) + } } /// Calculate crc-32 of the provided buffer with the initial CRC32 checksum of `crc`. @@ -293,9 +303,66 @@ unmangle!( /// /// Returns MZ_CRC32_INIT if ptr is `ptr::null`. pub unsafe extern "C" fn mz_crc32(crc: c_ulong, ptr: *const u8, buf_len: size_t) -> c_ulong { - ptr.as_ref().map_or(MZ_CRC32_INIT, |r| { - let data = slice::from_raw_parts(r, buf_len); + if ptr.is_null() { + MZ_CRC32_INIT + } else { + let data = slice::from_raw_parts(ptr, buf_len); mz_crc32_oxide(crc as u32, data) as c_ulong - }) + } } ); + +#[cfg(test)] +mod test { + use super::*; + use crate::tdef::Compressor; + + #[test] + fn miri_witness_stream_oxide_try_new_input_provenance() { + let data = *b"stream input"; + let mut stream = mz_stream { + next_in: data.as_ptr(), + avail_in: data.len() as c_uint, + data_type: StateTypeEnum::DeflateType, + ..Default::default() + }; + + // Under Miri this trips the raw-pointer-to-reference widening of the input buffer. + let stream_oxide = unsafe { StreamOxide::::try_new(&mut stream) }.unwrap(); + assert_eq!(stream_oxide.next_in.unwrap(), &data); + } + + #[test] + fn miri_witness_stream_oxide_try_new_output_provenance() { + let mut out = [0_u8; 16]; + let mut stream = mz_stream { + next_out: out.as_mut_ptr(), + avail_out: out.len() as c_uint, + data_type: StateTypeEnum::DeflateType, + ..Default::default() + }; + + // Under Miri this trips the raw-pointer-to-reference widening of the output buffer. + let mut stream_oxide = + unsafe { StreamOxide::::try_new(&mut stream) }.unwrap(); + assert_eq!(stream_oxide.next_out.as_mut().unwrap().len(), out.len()); + } + + #[test] + fn miri_witness_mz_adler32_input_provenance() { + let data = *b"adler witness"; + + // Under Miri this trips the raw-pointer-to-reference widening in `mz_adler32`. + let checksum = unsafe { mz_adler32(MZ_ADLER32_INIT as c_ulong, data.as_ptr(), data.len()) }; + assert_eq!(checksum as u32, mz_adler32_oxide(MZ_ADLER32_INIT, &data)); + } + + #[test] + fn miri_witness_mz_crc32_input_provenance() { + let data = *b"crc witness"; + + // Under Miri this trips the raw-pointer-to-reference widening in `mz_crc32`. + let checksum = unsafe { mz_crc32(MZ_CRC32_INIT, data.as_ptr(), data.len()) }; + assert_eq!(checksum as u32, mz_crc32_oxide(MZ_CRC32_INIT as u32, &data)); + } +} diff --git a/src/tdef.rs b/src/tdef.rs index 1898fbd..6a9b418 100644 --- a/src/tdef.rs +++ b/src/tdef.rs @@ -170,19 +170,17 @@ unmangle!( return tdefl_status::TDEFL_STATUS_BAD_PARAM; } - let in_slice = (in_buf as *const u8) - .as_ref() - .map_or(&[][..], |in_buf| slice::from_raw_parts(in_buf, in_buf_size)); + let in_slice = if in_buf_size == 0 { + &[] + } else { + slice::from_raw_parts(in_buf as *const u8, in_buf_size) + }; let res = match compressor_wrap.callback { - None => match (out_buf as *mut u8).as_mut() { - Some(out_buf) => compress( - compressor, - in_slice, - slice::from_raw_parts_mut(out_buf, out_buf_size), - flush, - ), - None => { + None => { + let out_slice = if out_buf_size == 0 { + &mut [] + } else if out_buf.is_null() { if let Some(size) = in_size { *size = 0 } @@ -190,8 +188,12 @@ unmangle!( *size = 0 } return tdefl_status::TDEFL_STATUS_BAD_PARAM; - } - }, + } else { + slice::from_raw_parts_mut(out_buf as *mut u8, out_buf_size) + }; + + compress(compressor, in_slice, out_slice, flush) + } Some(ref func) => { if out_buf_size > 0 || !out_buf.is_null() { if let Some(size) = in_size { @@ -205,7 +207,7 @@ unmangle!( let res = compress_to_output(compressor, in_slice, flush, |out: &[u8]| { (func.put_buf_func)( - &(out[0]) as *const u8 as *const c_void, + out.as_ptr().cast::(), out.len() as i32, func.put_buf_user, ) != 0 @@ -494,4 +496,44 @@ mod test { assert!(dec.as_slice() == &data[..]); } } + + #[test] + fn miri_witness_tdefl_compress_mem_to_heap_input_provenance() { + let data = b"miri witness"; + let mut out_len = 0; + // Under Miri this trips the raw-pointer-to-reference widening in `tdefl_compress`. + let out_data = unsafe { + tdefl_compress_mem_to_heap(data.as_ptr().cast::(), data.len(), &mut out_len, 0) + }; + assert!(!out_data.is_null()); + unsafe { + crate::miniz_def_free_func(ptr::null_mut(), out_data); + } + } + + #[test] + fn miri_witness_tdefl_compress_output_provenance() { + let mut compressor = Compressor::default(); + let init = unsafe { tdefl_init(Some(&mut compressor), None, ptr::null_mut(), 0) }; + assert!(init == tdefl_status::TDEFL_STATUS_OKAY); + + let mut in_size = 0; + let mut out_size = 64; + let mut out = [0_u8; 64]; + // Under Miri this trips the raw-pointer-to-reference widening of the output buffer. + let status = unsafe { + tdefl_compress( + Some(&mut compressor), + ptr::null(), + Some(&mut in_size), + out.as_mut_ptr() as *mut c_void, + Some(&mut out_size), + tdefl_flush::TDEFL_FINISH, + ) + }; + + assert!(status == tdefl_status::TDEFL_STATUS_DONE); + assert_eq!(in_size, 0); + assert!(out_size <= out.len()); + } }