Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 82 additions & 15 deletions src/c_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -282,20 +290,79 @@ 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`.
/// If c_ulong is wider than 32 bits, only the lower 32 bits will be used.
///
/// 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::<Compressor>::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::<Compressor>::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));
}
}
70 changes: 56 additions & 14 deletions src/tdef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,28 +170,30 @@ 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
}
if let Some(size) = out_size {
*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 {
Expand All @@ -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::<c_void>(),
out.len() as i32,
func.put_buf_user,
) != 0
Expand Down Expand Up @@ -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::<c_void>(), 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());
}
}