From 9b9682044427e79eb452d9f9935c11d2b3d83bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Fri, 31 May 2024 14:35:12 -0700 Subject: [PATCH] capi: Factor out util module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move a bunch of utility functionality into the newly introduced util module. In so doing we make CI test everything in this module under Miri. Signed-off-by: Daniel Müller --- capi/src/inspect.rs | 2 +- capi/src/lib.rs | 130 +---------------------------------------- capi/src/normalize.rs | 2 +- capi/src/symbolize.rs | 4 +- capi/src/util.rs | 131 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 132 deletions(-) create mode 100644 capi/src/util.rs diff --git a/capi/src/inspect.rs b/capi/src/inspect.rs index f54f3674..1d894535 100644 --- a/capi/src/inspect.rs +++ b/capi/src/inspect.rs @@ -29,7 +29,7 @@ use crate::blaze_err; use crate::blaze_err_last; use crate::from_cstr; use crate::set_last_err; -use crate::slice_from_user_array; +use crate::util::slice_from_user_array; /// C ABI compatible version of [`blazesym::inspect::Inspector`]. diff --git a/capi/src/lib.rs b/capi/src/lib.rs index 26ad0e8e..0f7825bd 100644 --- a/capi/src/lib.rs +++ b/capi/src/lib.rs @@ -47,7 +47,7 @@ macro_rules! input_zeroed { } else { let effective_size = memoffset::offset_of!($container_ty, reserved); unsafe { - crate::is_mem_zero( + crate::util::is_mem_zero( $container_ptr.cast::().add(effective_size), user_size.saturating_sub(effective_size), ) @@ -88,13 +88,10 @@ mod inspect; mod normalize; #[allow(non_camel_case_types)] mod symbolize; +mod util; -use std::borrow::Cow; use std::cell::Cell; use std::ffi::c_char; -use std::mem::align_of; -use std::ptr::NonNull; -use std::slice; use blazesym::ErrorKind; @@ -230,135 +227,12 @@ pub extern "C" fn blaze_err_str(err: blaze_err) -> *const c_char { } -/// Check whether the given piece of memory is zeroed out. -/// -/// # Safety -/// The caller needs to make sure that `mem` points to `len` (or more) bytes of -/// valid memory. -pub(crate) unsafe fn is_mem_zero(mut mem: *const u8, mut len: usize) -> bool { - while len > 0 { - if unsafe { mem.read() } != 0 { - return false - } - mem = unsafe { mem.add(1) }; - len -= 1; - } - true -} - -/// "Safely" create a slice from an aligned user provided array. -pub(crate) unsafe fn slice_from_aligned_user_array<'t, T>( - items: *const T, - num_items: usize, -) -> &'t [T] { - let items = if items.is_null() { - // `slice::from_raw_parts` requires a properly aligned non-NULL pointer. - // Craft one. - NonNull::dangling().as_ptr() - } else { - items - }; - unsafe { slice::from_raw_parts(items, num_items) } -} - -/// "Safely" create a slice from a user provided array. -pub(crate) unsafe fn slice_from_user_array<'t, T>(items: *const T, num_items: usize) -> Cow<'t, [T]> -where - T: Clone, -{ - #[cold] - fn safely_copy_to_allocated_slow(items: *const T, num_items: usize) -> Vec - where - T: Clone, - { - if items.is_null() { - Vec::new() - } else { - let mut src = items; - let mut buffer = Vec::::with_capacity(num_items); - let mut dst = buffer.as_mut_ptr(); - - for _ in 0..num_items { - let () = unsafe { dst.write(src.read_unaligned()) }; - src = unsafe { src.add(1) }; - dst = unsafe { dst.add(1) }; - } - let () = unsafe { buffer.set_len(num_items) }; - buffer - } - } - - if items.align_offset(align_of::()) == 0 { - let slice = unsafe { slice_from_aligned_user_array(items, num_items) }; - Cow::Borrowed(slice) - } else { - let vec = safely_copy_to_allocated_slow(items, num_items); - Cow::Owned(vec) - } -} - - #[cfg(test)] mod tests { use super::*; use std::ffi::CStr; - use std::ops::Deref as _; - use std::ptr; - - - /// Check that `is_mem_zero` works as it should. - #[test] - fn mem_zeroed_checking() { - let mut bytes = [0u8; 64]; - assert!( - unsafe { is_mem_zero(bytes.as_slice().as_ptr(), bytes.len()) }, - "{bytes:#x?}" - ); - - bytes[bytes.len() / 2] = 42; - assert!( - !unsafe { is_mem_zero(bytes.as_slice().as_ptr(), bytes.len()) }, - "{bytes:#x?}" - ); - } - - /// Test the `slice_from_aligned_user_array` helper in the presence of - /// various inputs. - #[test] - fn slice_creation() { - let slice = unsafe { slice_from_aligned_user_array::(ptr::null(), 0) }; - assert_eq!(slice, &[]); - let array = []; - let slice = - unsafe { slice_from_aligned_user_array::(&array as *const _, array.len()) }; - assert_eq!(slice, &[]); - - let array = [42u64, 1337]; - let slice = - unsafe { slice_from_aligned_user_array::(&array as *const _, array.len()) }; - assert_eq!(slice, &[42, 1337]); - } - - /// Make sure that we can create a slice from a potentially unaligned C - /// array of values. - #[test] - fn unaligned_slice_creation() { - let slice = unsafe { slice_from_user_array(ptr::null::(), 0) }; - assert_eq!(slice.deref(), &[]); - - let mut buffer = [0u64; 8]; - let ptr = unsafe { buffer.as_mut_ptr().byte_add(3) }; - - let slice = unsafe { slice_from_user_array(ptr, buffer.len() - 1) }; - assert!(matches!(slice, Cow::Owned(..)), "{slice:?}"); - assert_eq!(slice.len(), buffer.len() - 1); - - let slice = unsafe { slice_from_user_array(buffer.as_ptr(), buffer.len()) }; - assert!(matches!(slice, Cow::Borrowed(..)), "{slice:?}"); - assert_eq!(slice.len(), buffer.len()); - } /// Check that we can convert `ErrorKind` instances into `blaze_err`. #[test] diff --git a/capi/src/normalize.rs b/capi/src/normalize.rs index 502bfcf4..21b1b288 100644 --- a/capi/src/normalize.rs +++ b/capi/src/normalize.rs @@ -26,7 +26,7 @@ use crate::blaze_err; #[cfg(doc)] use crate::blaze_err_last; use crate::set_last_err; -use crate::slice_from_user_array; +use crate::util::slice_from_user_array; /// C ABI compatible version of [`blazesym::normalize::Normalizer`]. diff --git a/capi/src/symbolize.rs b/capi/src/symbolize.rs index 5ffb90d8..5bf3abc6 100644 --- a/capi/src/symbolize.rs +++ b/capi/src/symbolize.rs @@ -30,8 +30,8 @@ use crate::blaze_err; #[cfg(doc)] use crate::blaze_err_last; use crate::set_last_err; -use crate::slice_from_aligned_user_array; -use crate::slice_from_user_array; +use crate::util::slice_from_aligned_user_array; +use crate::util::slice_from_user_array; /// The parameters to load symbols and debug information from an ELF. diff --git a/capi/src/util.rs b/capi/src/util.rs new file mode 100644 index 00000000..ac1b8f3a --- /dev/null +++ b/capi/src/util.rs @@ -0,0 +1,131 @@ +use std::borrow::Cow; +use std::mem::align_of; +use std::ptr::NonNull; +use std::slice; + + +/// Check whether the given piece of memory is zeroed out. +/// +/// # Safety +/// The caller needs to make sure that `mem` points to `len` (or more) bytes of +/// valid memory. +pub(crate) unsafe fn is_mem_zero(mut mem: *const u8, mut len: usize) -> bool { + while len > 0 { + if unsafe { mem.read() } != 0 { + return false + } + mem = unsafe { mem.add(1) }; + len -= 1; + } + true +} + +/// "Safely" create a slice from an aligned user provided array. +pub(crate) unsafe fn slice_from_aligned_user_array<'t, T>( + items: *const T, + num_items: usize, +) -> &'t [T] { + let items = if items.is_null() { + // `slice::from_raw_parts` requires a properly aligned non-NULL pointer. + // Craft one. + NonNull::dangling().as_ptr() + } else { + items + }; + unsafe { slice::from_raw_parts(items, num_items) } +} + +/// "Safely" create a slice from a user provided array. +pub(crate) unsafe fn slice_from_user_array<'t, T>(items: *const T, num_items: usize) -> Cow<'t, [T]> +where + T: Clone, +{ + #[cold] + fn safely_copy_to_allocated_slow(items: *const T, num_items: usize) -> Vec + where + T: Clone, + { + if items.is_null() { + Vec::new() + } else { + let mut src = items; + let mut buffer = Vec::::with_capacity(num_items); + let mut dst = buffer.as_mut_ptr(); + + for _ in 0..num_items { + let () = unsafe { dst.write(src.read_unaligned()) }; + src = unsafe { src.add(1) }; + dst = unsafe { dst.add(1) }; + } + let () = unsafe { buffer.set_len(num_items) }; + buffer + } + } + + if items.align_offset(align_of::()) == 0 { + let slice = unsafe { slice_from_aligned_user_array(items, num_items) }; + Cow::Borrowed(slice) + } else { + let vec = safely_copy_to_allocated_slow(items, num_items); + Cow::Owned(vec) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + use std::ops::Deref as _; + use std::ptr; + + + /// Check that `is_mem_zero` works as it should. + #[test] + fn mem_zeroed_checking() { + let mut bytes = [0u8; 64]; + let zero = unsafe { is_mem_zero(bytes.as_slice().as_ptr(), bytes.len()) }; + assert!(zero, "{bytes:#x?}"); + + bytes[bytes.len() / 2] = 42; + let zero = unsafe { is_mem_zero(bytes.as_slice().as_ptr(), bytes.len()) }; + assert!(!zero, "{bytes:#x?}"); + } + + /// Test the `slice_from_aligned_user_array` helper in the presence of + /// various inputs. + #[test] + fn slice_creation() { + let slice = unsafe { slice_from_aligned_user_array::(ptr::null(), 0) }; + assert_eq!(slice, &[]); + + let array = []; + let slice = + unsafe { slice_from_aligned_user_array::(&array as *const _, array.len()) }; + assert_eq!(slice, &[]); + + let array = [42u64, 1337]; + let slice = + unsafe { slice_from_aligned_user_array::(&array as *const _, array.len()) }; + assert_eq!(slice, &[42, 1337]); + } + + /// Make sure that we can create a slice from a potentially unaligned C + /// array of values. + #[test] + fn unaligned_slice_creation() { + let slice = unsafe { slice_from_user_array(ptr::null::(), 0) }; + assert_eq!(slice.deref(), &[]); + + let mut buffer = [0u64; 8]; + let ptr = unsafe { buffer.as_mut_ptr().byte_add(3) }; + + let slice = unsafe { slice_from_user_array(ptr, buffer.len() - 1) }; + assert!(matches!(slice, Cow::Owned(..)), "{slice:?}"); + assert_eq!(slice.len(), buffer.len() - 1); + + let slice = unsafe { slice_from_user_array(buffer.as_ptr(), buffer.len()) }; + assert!(matches!(slice, Cow::Borrowed(..)), "{slice:?}"); + assert_eq!(slice.len(), buffer.len()); + } +}