diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 90ea168..25c3103 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,6 +1,6 @@ name: Rust -on: [push] +on: [push, pull_request] jobs: native: diff --git a/src/lib.rs b/src/lib.rs index 80529b0..e62b4ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -310,6 +310,7 @@ macro_rules! self_cell { $(#[$StructMeta])* $Vis struct $StructName $(<$OwnerLifetime>)* { unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell< + $StructName$(<$OwnerLifetime>)?, $Owner, $Dependent<'static> >, @@ -509,6 +510,7 @@ macro_rules! self_cell { let unsafe_self_cell = unsafe { core::mem::transmute::< Self, $crate::unsafe_self_cell::UnsafeSelfCell< + $StructName$(<$OwnerLifetime>)?, $Owner, $Dependent<'static> > diff --git a/src/unsafe_self_cell.rs b/src/unsafe_self_cell.rs index 61286df..0deb9b3 100644 --- a/src/unsafe_self_cell.rs +++ b/src/unsafe_self_cell.rs @@ -27,18 +27,24 @@ pub struct JoinedCell { // Library controlled struct that marks all accesses as unsafe. // Because the macro generated struct impl can be extended, could be unsafe. #[doc(hidden)] -pub struct UnsafeSelfCell { +pub struct UnsafeSelfCell { joined_void_ptr: NonNull, + // ContainedIn is necessary for type safety since we don't fully + // prohibit access to the UnsafeSelfCell; swapping between different + // structs can be unsafe otherwise, see Issue #17. + contained_in_marker: PhantomData, + owner_marker: PhantomData, // DependentStatic is only used to correctly derive Send and Sync. dependent_marker: PhantomData, } -impl UnsafeSelfCell { +impl UnsafeSelfCell { pub unsafe fn new(joined_void_ptr: NonNull) -> Self { Self { joined_void_ptr, + contained_in_marker: PhantomData, owner_marker: PhantomData, dependent_marker: PhantomData, } @@ -96,7 +102,8 @@ impl UnsafeSelfCell { } } -unsafe impl Send for UnsafeSelfCell +unsafe impl Send + for UnsafeSelfCell where // Only derive Send if Owner and DependentStatic is also Send Owner: Send, @@ -104,7 +111,8 @@ where { } -unsafe impl Sync for UnsafeSelfCell +unsafe impl Sync + for UnsafeSelfCell where // Only derive Sync if Owner and DependentStatic is also Sync Owner: Sync, diff --git a/tests/invalid/swap_cell_member.rs b/tests/invalid/swap_cell_member.rs new file mode 100644 index 0000000..2d88f1a --- /dev/null +++ b/tests/invalid/swap_cell_member.rs @@ -0,0 +1,37 @@ +use self_cell::self_cell; + +type Dep1<'a> = (&'a str, &'static str); + +self_cell! { + pub struct Struct1 { + owner: String, + #[covariant] + dependent: Dep1, + } +} + +type Dep2<'a> = (&'static str, &'a str); + +self_cell! { + pub struct Struct2 { + owner: String, + #[covariant] + dependent: Dep2, + } +} + +fn main() { + let hello: &'static str; + { + let mut x1 = Struct1::new(String::from("Hello World"), |s| (s, "")); + let mut x2 = Struct2::new(String::new(), |_| ("", "")); + std::mem::swap(&mut x1.unsafe_self_cell, &mut x2.unsafe_self_cell); + hello = x2.borrow_dependent().0; + + dbg!(hello); // "Hello World" + // hello is now a static reference in to the "Hello World" string + } + // the String is dropped at the end of the block above + + dbg!(hello); // prints garbage, use-after-free +} diff --git a/tests/invalid/swap_cell_member.stderr b/tests/invalid/swap_cell_member.stderr new file mode 100644 index 0000000..1223012 --- /dev/null +++ b/tests/invalid/swap_cell_member.stderr @@ -0,0 +1,8 @@ +error[E0308]: mismatched types + --> $DIR/swap_cell_member.rs:28:50 + | +28 | std::mem::swap(&mut x1.unsafe_self_cell, &mut x2.unsafe_self_cell); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Struct1`, found struct `Struct2` + | + = note: expected mutable reference `&mut UnsafeSelfCell` + found mutable reference `&mut UnsafeSelfCell`