From 1c941e0541cce5875baf405951543a0b2ca2c3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radek=20V=C3=ADt?= Date: Sun, 16 Jul 2023 21:20:47 +0200 Subject: [PATCH] Fix soundness hole by restricting T to 'static --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/prc.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/sync.rs | 60 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 131 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc87519..2dd755f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "pared" -version = "0.1.0" +version = "0.2.0" dependencies = [ "doc-comment", ] diff --git a/Cargo.toml b/Cargo.toml index b299e4d..8304db6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pared" -version = "0.1.0" +version = "0.2.0" authors = ["Radek Vít "] edition = "2021" rust-version = "1.56" diff --git a/src/prc.rs b/src/prc.rs index 18222df..37d6cb8 100644 --- a/src/prc.rs +++ b/src/prc.rs @@ -22,6 +22,52 @@ //! accepts_prc(from_u8); //! } //! ``` +//! //! # Soundness +//! None of the following should compile: +//! +//! ```compile_error +//! use pared::prc::Prc; +//! +//! let x: Prc<()> = Prc::new(()); +//! let z: Prc; +//! { +//! let s = "Hello World!".to_string(); +//! let s_ref: &str = &s; +//! let y: Prc<&str> = x.project(|_| &s_ref); +//! z = y.project(|s: &&str| *s); +//! // s deallocated here +//! } +//! println!("{}", &*z); // printing garbage, accessing `s` after it’s freed +//! ``` +//! +//! ```compile_error +//! use pared::prc::Prc; +//! +//! let x: Prc<()> = Prc::new(()); +//! let z: Prc; +//! { +//! let s = "Hello World!".to_string(); +//! let s_ref: &str = &s; +//! let y: Prc<&str> = x.project(|_| &s_ref); +//! z = y.project(|s: &&str| *s); +//! // s deallocated here +//! } +//! println!("{}", &*z); // printing garbage, accessing `s` after it’s freed +//! ``` +//! +//! ```compile_error +//! use pared::prc::Prc; +//! use std::sync::Arc; +//! +//! let x: Prc<()> = Prc::new(()); +//! let z: Prc; +//! { +//! let s = "Hello World!".to_string(); +//! z = x.project(|_| &s as &str); +//! // s deallocated here +//! } +//! println!("{}", &*z); // printing garbage, accessing `s` after it’s freed +//! ``` mod erased_rc; @@ -74,7 +120,10 @@ pub struct Prc { projected: NonNull, } -impl Prc { +impl Prc +where + T: 'static, +{ /// Constructs a new `Prc`. /// /// # Example @@ -112,6 +161,7 @@ impl Prc { pub fn from_rc(rc: &Rc, project: F) -> Self where U: ?Sized, + T: 'static, F: for<'x> FnOnce(&'x U) -> &'x T, { let projected = project(rc); @@ -124,6 +174,20 @@ impl Prc { } } + // pub fn from_rc(rc: &Rc, project: F) -> Prc<>::Output> + // where + // F: for<'x> ProjectOnce<'x, T>, + // { + // let projected = project.project_once(rc); + // // SAFETY: fn shouldn't be able to capture any local references + // // which should mean that the projection done by f is safe + // let projected = unsafe { NonNull::new_unchecked(projected as *const _ as *mut _) }; + // Prc::<>::Output> { + // rc: TypeErasedRc::new(rc.clone()), + // projected, + // } + // } + /// Constructs a new `Prc` from an existing `Prc` by projecting a field. /// /// # Panics @@ -145,6 +209,7 @@ impl Prc { /// ``` pub fn project(&self, project: F) -> Prc where + T: 'static, U: ?Sized, F: for<'x> FnOnce(&'x T) -> &'x U, { @@ -317,13 +382,20 @@ where } } -impl>> From for Prc { +impl From for Prc +where + T: ?Sized + 'static, + F: Into>, +{ fn from(value: F) -> Self { Prc::from_rc(&value.into(), |x| x) } } -impl FromIterator for Prc<[T]> { +impl FromIterator for Prc<[T]> +where + T: 'static, +{ fn from_iter>(iter: I) -> Self { iter.into_iter().collect::>().into() } diff --git a/src/sync.rs b/src/sync.rs index bd276c2..084bd0d 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -42,6 +42,53 @@ //! // Error //! let denied = no_send.project(|x| x); //! ``` +//! +//! # Soundness +//! None of the following should compile: +//! +//! ```compile_fail +//! use pared::sync::Parc; +//! +//! let x: Parc<()> = Parc::new(()); +//! let z: Parc; +//! { +//! let s = "Hello World!".to_string(); +//! let s_ref: &str = &s; +//! let y: Parc<&str> = x.project(|_| &s_ref); +//! z = y.project(|s: &&str| *s); +//! // s deallocated here +//! } +//! println!("{}", &*z); // printing garbage, accessing `s` after it’s freed +//! ``` +//! +//! ```compile_fail +//! use pared::sync::Parc; +//! +//! let x: Parc<()> = Parc::new(()); +//! let z: Parc; +//! { +//! let s = "Hello World!".to_string(); +//! let s_ref: &str = &s; +//! let y: Parc<&str> = x.project(|_| &s_ref); +//! z = y.project(|s: &&str| *s); +//! // s deallocated here +//! } +//! println!("{}", &*z); // printing garbage, accessing `s` after it’s freed +//! ``` +//! +//! ```compile_fail +//! use pared::sync::Parc; +//! use std::sync::Arc; +//! +//! let x: Parc<()> = Parc::new(()); +//! let z: Parc; +//! { +//! let s = "Hello World!".to_string(); +//! z = x.project(|_| &s as &str); +//! // s deallocated here +//! } +//! println!("{}", &*z); // printing garbage, accessing `s` after it’s freed +//! ``` mod erased_arc; @@ -116,7 +163,7 @@ pub struct Parc { impl Parc where - T: Send + Sync, + T: Send + Sync + 'static, { /// Constructs a new `Parc`. /// @@ -156,6 +203,7 @@ impl Parc { #[inline] pub fn from_arc(arc: &Arc, project: F) -> Self where + T: 'static, U: ?Sized + Send + Sync, F: for<'x> FnOnce(&'x U) -> &'x T, { @@ -191,10 +239,10 @@ impl Parc { /// let projected = parc.project(|tuple| &local); /// ``` #[inline] - pub fn project(&self, project: F) -> Parc + pub fn project<'a, U, F>(&'a self, project: F) -> Parc where - T: Send + Sync, - U: ?Sized, + T: Send + Sync + 'static, + U: ?Sized + 'static, F: for<'x> FnOnce(&'x T) -> &'x U, { let projected = project(self); @@ -390,7 +438,7 @@ where impl From for Parc where - T: ?Sized + Send + Sync, + T: ?Sized + Send + Sync + 'static, F: Into>, { #[inline] @@ -401,7 +449,7 @@ where impl FromIterator for Parc<[T]> where - T: Send + Sync, + T: Send + Sync + 'static, { #[inline] fn from_iter>(iter: I) -> Self {