Skip to content

Commit

Permalink
Fix soundness hole by restricting T to 'static
Browse files Browse the repository at this point in the history
  • Loading branch information
radekvit committed Jul 16, 2023
1 parent de53907 commit 1c941e0
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pared"
version = "0.1.0"
version = "0.2.0"
authors = ["Radek Vít <[email protected]>"]
edition = "2021"
rust-version = "1.56"
Expand Down
78 changes: 75 additions & 3 deletions src/prc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<str>;
//! {
//! 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<str>;
//! {
//! 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<str>;
//! {
//! 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;

Expand Down Expand Up @@ -74,7 +120,10 @@ pub struct Prc<T: ?Sized> {
projected: NonNull<T>,
}

impl<T> Prc<T> {
impl<T> Prc<T>
where
T: 'static,
{
/// Constructs a new `Prc<T>`.
///
/// # Example
Expand Down Expand Up @@ -112,6 +161,7 @@ impl<T: ?Sized> Prc<T> {
pub fn from_rc<U, F>(rc: &Rc<U>, project: F) -> Self
where
U: ?Sized,
T: 'static,
F: for<'x> FnOnce(&'x U) -> &'x T,
{
let projected = project(rc);
Expand All @@ -124,6 +174,20 @@ impl<T: ?Sized> Prc<T> {
}
}

// pub fn from_rc<F>(rc: &Rc<T>, project: F) -> Prc<<F as ProjectOnce<'_, T>>::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::<<F as ProjectOnce<'_, T>>::Output> {
// rc: TypeErasedRc::new(rc.clone()),
// projected,
// }
// }

/// Constructs a new `Prc<T>` from an existing `Prc<T>` by projecting a field.
///
/// # Panics
Expand All @@ -145,6 +209,7 @@ impl<T: ?Sized> Prc<T> {
/// ```
pub fn project<U, F>(&self, project: F) -> Prc<U>
where
T: 'static,
U: ?Sized,
F: for<'x> FnOnce(&'x T) -> &'x U,
{
Expand Down Expand Up @@ -317,13 +382,20 @@ where
}
}

impl<T: ?Sized, F: Into<Rc<T>>> From<F> for Prc<T> {
impl<T, F> From<F> for Prc<T>
where
T: ?Sized + 'static,
F: Into<Rc<T>>,
{
fn from(value: F) -> Self {
Prc::from_rc(&value.into(), |x| x)
}
}

impl<T> FromIterator<T> for Prc<[T]> {
impl<T> FromIterator<T> for Prc<[T]>
where
T: 'static,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
iter.into_iter().collect::<Rc<[T]>>().into()
}
Expand Down
60 changes: 54 additions & 6 deletions src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<str>;
//! {
//! 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<str>;
//! {
//! 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<str>;
//! {
//! 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;

Expand Down Expand Up @@ -116,7 +163,7 @@ pub struct Parc<T: ?Sized> {

impl<T> Parc<T>
where
T: Send + Sync,
T: Send + Sync + 'static,
{
/// Constructs a new `Parc<T>`.
///
Expand Down Expand Up @@ -156,6 +203,7 @@ impl<T: ?Sized> Parc<T> {
#[inline]
pub fn from_arc<U, F>(arc: &Arc<U>, project: F) -> Self
where
T: 'static,
U: ?Sized + Send + Sync,
F: for<'x> FnOnce(&'x U) -> &'x T,
{
Expand Down Expand Up @@ -191,10 +239,10 @@ impl<T: ?Sized> Parc<T> {
/// let projected = parc.project(|tuple| &local);
/// ```
#[inline]
pub fn project<U, F>(&self, project: F) -> Parc<U>
pub fn project<'a, U, F>(&'a self, project: F) -> Parc<U>
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);
Expand Down Expand Up @@ -390,7 +438,7 @@ where

impl<T, F> From<F> for Parc<T>
where
T: ?Sized + Send + Sync,
T: ?Sized + Send + Sync + 'static,
F: Into<Arc<T>>,
{
#[inline]
Expand All @@ -401,7 +449,7 @@ where

impl<T> FromIterator<T> for Parc<[T]>
where
T: Send + Sync,
T: Send + Sync + 'static,
{
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Expand Down

0 comments on commit 1c941e0

Please sign in to comment.