pub mod xor_intrinsic;
use std::sync::atomic::{fence, Ordering};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ptr::NonNull;
unsafe fn xor_chunks<T>(data: *mut u8, key: *const u8) {
unsafe {
xor_intrinsic::xor_chunks_intrinsic_baseline::<T>(data, key);
}
fence(Ordering::SeqCst);
}
pub struct MangledBoxArbitrary<T> {
data: Box<MaybeUninit<T>>,
key: MaybeUninit<T>,
}
impl<T> MangledBoxArbitrary<T> {
pub fn new() -> Self {
let data = Box::new_zeroed();
let mut key = MaybeUninit::uninit();
getrandom::fill_uninit(key.as_bytes_mut()).expect("no keygen");
Self { data, key }
}
pub fn rekey(&mut self) {
let mut diff_key = MaybeUninit::<T>::uninit();
getrandom::fill_uninit(diff_key.as_bytes_mut()).expect("no keygen");
unsafe {
xor_chunks::<T>(
Box::as_mut_ptr(&mut self.data).cast::<u8>(),
diff_key.as_ptr().cast::<u8>(),
);
xor_chunks::<T>(
self.key.as_mut_ptr().cast::<u8>(),
diff_key.as_ptr().cast::<u8>(),
);
}
}
pub(crate) fn with_mangled<F, R>(&mut self, f: F) -> R
where
F: FnOnce(NonNull<T>) -> R {
let data_ptr: *mut T = Box::as_mut_ptr(&mut self.data).cast::<T>();
f(NonNull::new(data_ptr).unwrap())
}
pub fn with_unmangled<F, R>(&mut self, f: F) -> R
where
F: FnOnce(NonNull<T>) -> R,
{
let data_ptr = Box::as_mut_ptr(&mut self.data).cast::<u8>();
let key_ptr = self.key.as_ptr().cast::<u8>();
let data_nn: NonNull<u8> = NonNull::new(data_ptr).unwrap();
unsafe {
xor_chunks::<T>(data_ptr, key_ptr);
}
struct RemangleGuard<T> {
data: *mut u8,
key: *const u8,
token: PhantomData<T>,
}
impl<T> Drop for RemangleGuard<T> {
fn drop(&mut self) {
unsafe { xor_chunks::<T>(self.data, self.key) }
}
}
let _guard = RemangleGuard::<T> {
data: data_ptr,
key: key_ptr,
token: PhantomData,
};
f(data_nn.cast())
}
pub unsafe fn drop_in_place(&mut self) {
self.with_unmangled(|p| unsafe { p.drop_in_place() });
}
}
impl<T> Default for MangledBoxArbitrary<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Drop for MangledBoxArbitrary<T> {
fn drop(&mut self) {
let data_ptr = Box::as_mut_ptr(&mut self.data).cast::<u8>();
let key_ptr = self.key.as_mut_ptr().cast::<u8>();
unsafe {
xor_chunks::<T>(data_ptr, data_ptr);
xor_chunks::<T>(key_ptr, key_ptr);
}
}
}
#[cfg(all(test, not(miri)))]
mod tests {
use std::clone::CloneToUninit;
use std::cell::RefCell;
use std::ptr::NonNull;
use std::rc::Rc;
use super::MangledBoxArbitrary as MangledBox;
fn ensure_send<T: Send>(_v: &T) {}
fn ensure_sync<T: Sync>(_v: &T) {}
#[test]
fn zst() {
let mut empty_box = MangledBox::<()>::new();
ensure_send(&empty_box);
ensure_sync(&empty_box);
empty_box.with_unmangled(|_| {});
}
#[derive(Clone, Copy)]
#[repr(C, align(64))]
struct Align64;
#[test]
fn overaligned_zst() {
let mut align64_box = MangledBox::<Align64>::new();
ensure_send(&align64_box);
ensure_sync(&align64_box);
align64_box.with_unmangled(|p| {
assert_eq!(
p.as_ptr().align_offset(64),
0,
"alignment not preserved on overaligned ZST type"
);
});
}
struct ReportDrop(Rc<RefCell<bool>>);
impl Drop for ReportDrop {
fn drop(&mut self) {
*self.0.borrow_mut() = true;
}
}
#[test]
fn drop_at_correct_time() {
let drop_reported = Rc::new(RefCell::new(false));
let drop_reported_clone = drop_reported.clone();
{
let mut box_ = MangledBox::<ReportDrop>::new();
box_.with_unmangled(|p: NonNull<ReportDrop>| {
let place: *mut u8 = p.as_ptr().cast();
unsafe { drop_reported_clone.clone_to_uninit(place) };
});
assert!(!*drop_reported.borrow(), "dropped a live box");
unsafe {
box_.drop_in_place();
}
assert!(*drop_reported.borrow(), "did not receive drop report");
}
}
#[test]
fn no_auto_drop() {
let drop_reported = Rc::new(RefCell::new(false));
let drop_reported_clone = drop_reported.clone();
{
let mut box_ = MangledBox::<ReportDrop>::new();
box_.with_unmangled(|p: NonNull<ReportDrop>| {
let place: *mut u8 = p.as_ptr().cast();
unsafe { drop_reported_clone.clone_to_uninit(place) };
});
assert!(!*drop_reported.borrow(), "dropped a live box");
}
assert!(
!*drop_reported.borrow(),
"box forwarded drop when it could not prove content is initialized"
);
}
#[test]
fn real_structures_string() {
use std::fmt::Write;
let mut box_ = MangledBox::<String>::new();
box_.with_unmangled(|p| unsafe {
p.write("hello".to_owned());
});
box_.with_unmangled(|mut p| {
assert_eq!(unsafe { p.as_ref() }, "hello");
unsafe {
p.as_mut().push_str(" Rust!");
}
});
box_.rekey();
box_.with_unmangled(|p| {
let mut s = String::with_capacity(13);
write!(s, "{:?}", unsafe { p.as_ref() }).unwrap();
assert_eq!(s, "\"hello Rust!\"");
});
unsafe {
box_.drop_in_place();
}
}
}