Skip to content

Commit

Permalink
capi: Factor out util module
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
d-e-s-o committed May 31, 2024
1 parent e30027e commit 9b96820
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 132 deletions.
2 changes: 1 addition & 1 deletion capi/src/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`].
Expand Down
130 changes: 2 additions & 128 deletions capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u8>().add(effective_size),
user_size.saturating_sub(effective_size),
)
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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<T>(items: *const T, num_items: usize) -> Vec<T>
where
T: Clone,
{
if items.is_null() {
Vec::new()
} else {
let mut src = items;
let mut buffer = Vec::<T>::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::<T>()) == 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::<u64>(ptr::null(), 0) };
assert_eq!(slice, &[]);

let array = [];
let slice =
unsafe { slice_from_aligned_user_array::<u64>(&array as *const _, array.len()) };
assert_eq!(slice, &[]);

let array = [42u64, 1337];
let slice =
unsafe { slice_from_aligned_user_array::<u64>(&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::<u64>(), 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]
Expand Down
2 changes: 1 addition & 1 deletion capi/src/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`].
Expand Down
4 changes: 2 additions & 2 deletions capi/src/symbolize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
131 changes: 131 additions & 0 deletions capi/src/util.rs
Original file line number Diff line number Diff line change
@@ -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<T>(items: *const T, num_items: usize) -> Vec<T>
where
T: Clone,
{
if items.is_null() {
Vec::new()
} else {
let mut src = items;
let mut buffer = Vec::<T>::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::<T>()) == 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::<u64>(ptr::null(), 0) };
assert_eq!(slice, &[]);

let array = [];
let slice =
unsafe { slice_from_aligned_user_array::<u64>(&array as *const _, array.len()) };
assert_eq!(slice, &[]);

let array = [42u64, 1337];
let slice =
unsafe { slice_from_aligned_user_array::<u64>(&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::<u64>(), 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());
}
}

0 comments on commit 9b96820

Please sign in to comment.