Skip to content

Commit

Permalink
Fix soundness issues proposed by #4
Browse files Browse the repository at this point in the history
This commit makes an initial attempt at resolving soundness issues in
the API by bringing runtime alignment data to all types.

Even reference types like ValueRef can use this information to be able
to allocate without intantiating the concrete underlying type.
  • Loading branch information
elrnv committed Oct 2, 2020
1 parent c133056 commit 6972f94
Show file tree
Hide file tree
Showing 12 changed files with 1,286 additions and 742 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ keywords = ["dyn", "trait", "vec", "any"]
travis-ci = { repository = "elrnv/dync", branch = "master" }

[dependencies]
reinterpret = "0.2"
downcast-rs = "1.1.1"
dync-derive = { path = "dync-derive", version = "0.4", optional = true }
num-traits = { version = "0.2", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ where
// memory layout throughout the execution of the program.
//
// For non-Copy types, memory safety is ensured by effectively forgetting reinterpreted memory,
// thus inhibiting any drop calls, which would otherwise could cause dangling references when the
// thus inhibiting any drop calls, which could otherwise cause dangling references when the
// memory is reinterpreted back into the original type.
//
// In summary, reinterpreting types as bytes is dangerous, but it should not cause undefined
Expand Down
125 changes: 124 additions & 1 deletion src/copy_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,88 @@ use crate::bytes::*;
use crate::vtable::*;
use crate::CopyElem;

macro_rules! generate_aligned_types {
($($t:ident($n:expr),)*) => {
$(
#[derive(Copy, Clone, Debug, Default)]
#[repr(align($n))]
pub(crate) struct $t(u8);
)*
}
}

generate_aligned_types!(
T0(1),
T1(2),
T2(4),
T3(8),
T4(16),
T5(32),
T6(64),
T7(128),
T8(256),
T9(512),
T10(1024),
T11(2048),
T12(4096),
T13(8192),
T14(16384),
T15(32768),
T16(65536),
T17(131072),
T18(262144),
T19(524288),
T20(1048576),
T21(2097152),
T22(4194304),
T23(8388608),
T24(16777216),
T25(33554432),
T26(67108864),
T27(134217728),
T28(268435456),
T29(536870912),
);

#[macro_export]
macro_rules! eval_align {
($a:expr; $fn:ident ::<_ $(,$params:ident)*>($($args:expr),*) ) => {
match $a {
1 => $fn::<T0, $($params,)*>($($args,)*),
2 => $fn::<T1, $($params,)*>($($args,)*),
4 => $fn::<T2, $($params,)*>($($args,)*),
8 => $fn::<T3, $($params,)*>($($args,)*),
16 => $fn::<T4, $($params,)*>($($args,)*),
32 => $fn::<T5, $($params,)*>($($args,)*),
64 => $fn::<T6, $($params,)*>($($args,)*),
128 => $fn::<T7, $($params,)*>($($args,)*),
256 => $fn::<T8, $($params,)*>($($args,)*),
512 => $fn::<T9, $($params,)*>($($args,)*),
1024 => $fn::<T10, $($params,)*>($($args,)*),
2048 => $fn::<T11, $($params,)*>($($args,)*),
4096 => $fn::<T12, $($params,)*>($($args,)*),
8192 => $fn::<T13, $($params,)*>($($args,)*),
16384 => $fn::<T14, $($params,)*>($($args,)*),
32768 => $fn::<T15, $($params,)*>($($args,)*),
65536 => $fn::<T16, $($params,)*>($($args,)*),
131072 => $fn::<T17, $($params,)*>($($args,)*),
262144 => $fn::<T18, $($params,)*>($($args,)*),
524288 => $fn::<T19, $($params,)*>($($args,)*),
1048576 => $fn::<T20, $($params,)*>($($args,)*),
2097152 => $fn::<T21, $($params,)*>($($args,)*),
4194304 => $fn::<T22, $($params,)*>($($args,)*),
8388608 => $fn::<T23, $($params,)*>($($args,)*),
16777216 => $fn::<T24, $($params,)*>($($args,)*),
33554432 => $fn::<T25, $($params,)*>($($args,)*),
67108864 => $fn::<T26, $($params,)*>($($args,)*),
134217728 => $fn::<T27, $($params,)*>($($args,)*),
268435456 => $fn::<T28, $($params,)*>($($args,)*),
536870912 => $fn::<T29, $($params,)*>($($args,)*),
_ => unreachable!("Unsupported alignment detected")
}
}
}

// Implement the basis for all value types.
macro_rules! impl_value_base {
() => {
Expand All @@ -20,6 +102,12 @@ macro_rules! impl_value_base {
self.type_id
}

/// Get the alignment of the referenced value.
#[inline]
pub fn value_alignment(&self) -> usize {
self.alignment
}

/// Returns `true` if this referenced value's type is the same as `T`.
#[inline]
pub fn is<T: 'static>(&self) -> bool {
Expand Down Expand Up @@ -47,6 +135,12 @@ pub trait GetBytesMut: GetBytesRef {
fn get_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>];
}

// Bytes cannot be dropped willy-nilly. Dropping triggers deallocators, and they must know about
// about the size and alignment of the original allocation.
pub trait DropAsAligned {
fn drop_as_aligned(&mut self, alignment: usize);
}

impl GetBytesRef for Box<[MaybeUninit<u8>]> {
#[inline]
fn get_bytes_ref(&self) -> &[MaybeUninit<u8>] {
Expand All @@ -60,6 +154,16 @@ impl GetBytesMut for Box<[MaybeUninit<u8>]> {
}
}

impl DropAsAligned for Box<[MaybeUninit<u8>]> {
#[inline]
fn drop_as_aligned(&mut self, alignment: usize) {
fn drop_bytes<T: 'static>(b: Box<[MaybeUninit<u8>]>) {
let _ = unsafe { Box::from_raw(Box::into_raw(b) as *mut T) };
}
eval_align!(alignment; drop_bytes::<_>(std::mem::take(self)));
}
}

impl GetBytesRef for MaybeUninit<usize> {
#[inline]
fn get_bytes_ref(&self) -> &[MaybeUninit<u8>] {
Expand All @@ -73,6 +177,13 @@ impl GetBytesMut for MaybeUninit<usize> {
}
}

impl DropAsAligned for MaybeUninit<usize> {
#[inline]
fn drop_as_aligned(&mut self, _: usize) {
// Value lives on the stack, no fancy deallocation necessary.
}
}

impl GetBytesRef for [MaybeUninit<u8>] {
#[inline]
fn get_bytes_ref(&self) -> &[MaybeUninit<u8>] {
Expand All @@ -94,6 +205,7 @@ where
{
pub(crate) bytes: &'a [MaybeUninit<u8>],
pub(crate) type_id: TypeId,
pub(crate) alignment: usize,
pub(crate) vtable: VTableRef<'a, V>,
}

Expand All @@ -107,6 +219,7 @@ impl<'a, V> CopyValueRef<'a, V> {
CopyValueRef {
bytes: typed.as_bytes(),
type_id: TypeId::of::<T>(),
alignment: std::mem::align_of::<T>(),
vtable: VTableRef::Box(Box::new(V::build_vtable())),
}
}
Expand All @@ -124,11 +237,13 @@ impl<'a, V: ?Sized> CopyValueRef<'a, V> {
pub(crate) unsafe fn from_raw_parts(
bytes: &'a [MaybeUninit<u8>],
type_id: TypeId,
alignment: usize,
vtable: impl Into<VTableRef<'a, V>>,
) -> CopyValueRef<'a, V> {
CopyValueRef {
bytes,
type_id,
alignment,
vtable: vtable.into(),
}
}
Expand All @@ -148,6 +263,7 @@ impl<'a, V: ?Sized> CopyValueRef<'a, V> {
CopyValueRef {
bytes: self.bytes,
type_id: self.type_id,
alignment: self.alignment,
vtable: VTableRef::Box(Box::new(U::from(self.vtable.take()))),
}
}
Expand All @@ -161,6 +277,7 @@ impl<'a, V: ?Sized> CopyValueRef<'a, V> {
CopyValueRef {
bytes: self.bytes,
type_id: self.type_id,
alignment: self.alignment,
vtable: VTableRef::Box(Box::new(U::from((*vtable).clone()))),
}
}
Expand All @@ -173,6 +290,7 @@ where
{
pub(crate) bytes: &'a mut [MaybeUninit<u8>],
pub(crate) type_id: TypeId,
pub(crate) alignment: usize,
pub(crate) vtable: VTableRef<'a, V>,
}

Expand All @@ -186,6 +304,7 @@ impl<'a, V> CopyValueMut<'a, V> {
CopyValueMut {
bytes: typed.as_bytes_mut(),
type_id: TypeId::of::<T>(),
alignment: std::mem::align_of::<T>(),
vtable: VTableRef::Box(Box::new(V::build_vtable())),
}
}
Expand All @@ -203,11 +322,13 @@ impl<'a, V: ?Sized> CopyValueMut<'a, V> {
pub(crate) unsafe fn from_raw_parts(
bytes: &'a mut [MaybeUninit<u8>],
type_id: TypeId,
alignment: usize,
vtable: impl Into<VTableRef<'a, V>>,
) -> CopyValueMut<'a, V> {
CopyValueMut {
bytes,
type_id,
alignment,
vtable: vtable.into(),
}
}
Expand All @@ -230,7 +351,7 @@ impl<'a, V: ?Sized> CopyValueMut<'a, V> {
pub fn swap(&mut self, other: &mut CopyValueMut<V>) {
if self.value_type_id() == other.value_type_id() {
self.bytes.swap_with_slice(&mut other.bytes);
std::mem::swap(&mut self.type_id, &mut other.type_id);
assert_eq!(self.alignment, other.alignment);
}
}

Expand All @@ -250,6 +371,7 @@ impl<'a, V: ?Sized> CopyValueMut<'a, V> {
CopyValueMut {
bytes: self.bytes,
type_id: self.type_id,
alignment: self.alignment,
vtable: VTableRef::Box(Box::new(U::from(vtable.clone()))),
}
}
Expand All @@ -261,6 +383,7 @@ impl<'a, V> From<CopyValueMut<'a, V>> for CopyValueRef<'a, V> {
CopyValueRef {
bytes: v.bytes,
type_id: v.type_id,
alignment: v.alignment,
vtable: v.vtable,
}
}
Expand Down
43 changes: 24 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub use slice_copy::*;
pub use slice_drop::*;
#[cfg(feature = "traits")]
pub use value::*;
pub use vec_copy::*;
pub use vec_copy::{CopyElem, VecCopy};
#[cfg(feature = "traits")]
pub use vec_drop::*;
pub use vtable::*;
Expand Down Expand Up @@ -85,11 +85,11 @@ macro_rules! from_dyn {
(@owned $vec:ident < dyn $trait:path as $vtable:path>) => {{
fn from_dyn<V: $trait>(vec: $crate::$vec<dyn $trait>) -> $crate::$vec<V> {
unsafe {
let (data, size, id, vtable) = vec.into_raw_parts();
let (data, vtable) = vec.into_raw_parts();
// If vtables were shared with Rc, we would use this:
//let updated_vtable: std::rc::Rc<V> = vtable.downcast_rc().ok().unwrap();
let updated_vtable: Box<V> = vtable.downcast().ok().unwrap();
$vec::from_raw_parts(data, size, id, updated_vtable)
$vec::from_raw_parts(data, updated_vtable)
}
}

Expand All @@ -98,20 +98,20 @@ macro_rules! from_dyn {
(@slice $slice:ident < dyn $trait:path >) => {{
fn from_dyn<'a, V: ?Sized + HasDrop + std::any::Any>(slice: $crate::$slice<'a, V>) -> $crate::$slice<'a, $vtable> {
unsafe {
let (data, size, id, vtable) = slice.into_raw_parts();
let (data, elem, vtable) = slice.into_raw_parts();
match vtable {
$crate::VTableRef::Ref(v) => {
let updated_vtable: &$vtable = v.downcast_ref().unwrap();
$slice::from_raw_parts(data, size, id, updated_vtable)
$slice::from_raw_parts(data, elem, updated_vtable)
}
$crate::VTableRef::Box(v) => {
let updated_vtable: Box<$vtable> = v.downcast().unwrap();
$slice::from_raw_parts(data, size, id, updated_vtable)
$slice::from_raw_parts(data, elem, updated_vtable)
}
#[cfg(feature = "shared-vtables")]
$crate::VTableRef::Rc(v) => {
let updated_vtable: std::rc::Rc<$vtable> = v.downcast().unwrap();
$slice::from_raw_parts(data, size, id, updated_vtable)
$slice::from_raw_parts(data, elem, updated_vtable)
}
}
}
Expand Down Expand Up @@ -146,11 +146,11 @@ macro_rules! into_dyn {
(@owned $vec:ident < dyn $trait:ident >) => {{
fn into_dyn<V: 'static + $trait>(vec: $crate::$vec<V>) -> $crate::$vec<dyn $trait> {
unsafe {
let (data, size, id, vtable) = vec.into_raw_parts();
let (data, vtable) = vec.into_raw_parts();
// If vtables were shared with Rc, we would use this:
//let updated_vtable: std::rc::Rc<dyn $trait> = vtable;
let updated_vtable: Box<dyn $trait> = vtable;
$vec::from_raw_parts(data, size, id, updated_vtable)
$vec::from_raw_parts(data, updated_vtable)
}
}

Expand All @@ -159,20 +159,20 @@ macro_rules! into_dyn {
(@slice $slice:ident < dyn $trait:path >) => {{
fn into_dyn<'a, V: 'static + $trait>(slice: $crate::$slice<'a, V>) -> $crate::$slice<'a, dyn $trait> {
unsafe {
let (data, size, id, vtable) = slice.into_raw_parts();
let (data, elem, vtable) = slice.into_raw_parts();
match vtable {
$crate::VTableRef::Ref(v) => {
let updated_vtable: &dyn $trait = v;
$slice::from_raw_parts(data, size, id, updated_vtable)
$slice::from_raw_parts(data, elem, updated_vtable)
}
$crate::VTableRef::Box(v) => {
let updated_vtable: Box<dyn $trait> = v;
$slice::from_raw_parts(data, size, id, updated_vtable)
$slice::from_raw_parts(data, elem, updated_vtable)
}
#[cfg(feature = "shared-vtables")]
$crate::VTableRef::Rc(v) => {
let updated_vtable: std::rc::Rc<dyn $trait> = v;
$slice::from_raw_parts(data, size, id, updated_vtable)
$slice::from_raw_parts(data, elem, updated_vtable)
}
}
}
Expand Down Expand Up @@ -208,11 +208,11 @@ pub(crate) trait ElementBytes {
/// contiguous byte slices.
pub(crate) trait ElementBytesMut: ElementBytes {
/// Get the mutable slice of bytes representing all the elements.
fn bytes_mut(&mut self) -> &mut [std::mem::MaybeUninit<u8>];
unsafe fn bytes_mut(&mut self) -> &mut [std::mem::MaybeUninit<u8>];

/// Index into a mutable slice of bytes.
#[inline]
fn index_byte_slice_mut(&mut self, i: usize) -> &mut [std::mem::MaybeUninit<u8>] {
unsafe fn index_byte_slice_mut(&mut self, i: usize) -> &mut [std::mem::MaybeUninit<u8>] {
let rng = self.index_byte_range(i);
&mut self.bytes_mut()[rng]
}
Expand All @@ -229,12 +229,17 @@ pub(crate) trait ElementBytesMut: ElementBytes {
let r_rng = self.index_byte_range(0);
if i < j {
let l_rng = self.index_byte_range(i);
let (l, r) = self.bytes_mut().split_at_mut(element_size * j);
l[l_rng].swap_with_slice(&mut r[r_rng])
// SAFETY: it is safe to swap aligned data since we have unique access.
unsafe {
let (l, r) = self.bytes_mut().split_at_mut(element_size * j);
l[l_rng].swap_with_slice(&mut r[r_rng])
}
} else {
let l_rng = self.index_byte_range(j);
let (l, r) = self.bytes_mut().split_at_mut(element_size * i);
l[l_rng].swap_with_slice(&mut r[r_rng])
unsafe {
let (l, r) = self.bytes_mut().split_at_mut(element_size * i);
l[l_rng].swap_with_slice(&mut r[r_rng])
}
}
}
}
Loading

0 comments on commit 6972f94

Please sign in to comment.