summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml5
-rw-r--r--src/lib.rs88
-rw-r--r--tests/trybuild.rs5
-rw-r--r--tests/ui/example_need_cell.rs12
-rw-r--r--tests/ui/example_need_cell.stderr5
-rw-r--r--tests/ui/example_opaque_vs_drop.rs25
-rw-r--r--tests/ui/example_opaque_vs_drop.stderr15
-rw-r--r--tests/ui/no_uaf_1.rs44
-rw-r--r--tests/ui/no_uaf_1.stderr37
9 files changed, 178 insertions, 58 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a8f7b7e..672d5d7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "selfref"
-version = "0.2.4"
+version = "0.3.0"
 authors = ["SoniEx2 <endermoneymod@gmail.com>"]
 edition = "2021"
 description = "Semi-pain-free self-referential pinned types"
@@ -17,3 +17,6 @@ nightly = []
 alloc = []
 
 [dependencies]
+
+[dev-dependencies]
+trybuild = "1.0.72"
diff --git a/src/lib.rs b/src/lib.rs
index b8a4f86..e87784d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -86,7 +86,6 @@
 //! use selfref::Holder;
 //! use selfref::new_with_closure;
 //! # use selfref::opaque;
-//! use selfref::operate_in_closure;
 //! #
 //! # struct MySelfRefStruct<'this> {
 //! #     this: Cell<Option<&'this MySelfRefStruct<'this>>>,
@@ -112,9 +111,9 @@
 //!
 //!     // then, build the self-reference
 //!     holder.as_ref().operate_in(
-//!         operate_in_closure::<MySelfRefStructKey, _, _>(|this| {
+//!         |this| {
 //!             this.this.set(Some(this.get_ref()));
-//!         })
+//!         }
 //!     );
 //! }
 //! ```
@@ -133,7 +132,6 @@
 //! 
 //! use selfref::Holder;
 //! use selfref::NewWith;
-//! use selfref::OperateIn;
 //! use selfref::opaque;
 //!
 //! struct Foo<'a, 'b: 'a> {
@@ -167,16 +165,9 @@
 //!     // construct the struct
 //!     let holder = pin!(Holder::new_with(FooBuilder(stack_str)));
 //!
-//!     // setup the self-reference
-//!     struct SetFooRef;
-//!     impl<'k, 'b: 'k> OperateIn<'k, FooKey<'b>> for SetFooRef {
-//!         type Out = ();
-//!         fn operate_in<'a>(self, foo: Pin<&'a Foo<'a, 'b>>)
-//!         where 'k: 'a {
-//!             foo.foo.set(Some(foo.get_ref()));
-//!         }
-//!     }
-//!     holder.as_ref().operate_in(SetFooRef);
+//!     holder.as_ref().operate_in(|foo| {
+//!         foo.foo.set(Some(foo.get_ref()));
+//!     });
 //! }
 //! ```
 //!
@@ -577,32 +568,19 @@ impl<'k, T> Holder<'k, T> where T: Opaque {
     }
 }
 
-/// Helper trait for working with a [`Holder`].
+/// Wrapper around a `Pin<&'k T::Kind<'k>>` for implied bounds.
 ///
-/// This is necessary because closures don't work properly here.
-///
-/// See [`Holder::operate_in`] for examples.
-pub trait OperateIn<'k, T: Opaque + 'k> {
-    /// The value returned by this operation.
-    type Out;
-    /// Do this operation.
-    fn operate_in<'a>(self, x: Pin<&'a T::Kind<'a>>) -> Self::Out where 'k: 'a;
+/// Derefs to `Pin<&'k T::Kind<'k>>`.
+pub struct OperateIn<'k, T> where T: Opaque + 'k {
+    inner: Pin<&'k T::Kind<'k>>,
 }
 
-impl<'k, T: Opaque + 'k, F, R> OperateIn<'k, T> for F
-where F: for<'a> FnOnce(Pin<&'a T::Kind<'a>>) -> R {
-    type Out = R;
-    fn operate_in<'a>(self, x: Pin<&'a T::Kind<'a>>) -> R where 'k: 'a {
-        self(x)
-    }
-}
+impl<'k, T> core::ops::Deref for OperateIn<'k, T> where T: Opaque {
+    type Target = Pin<&'k T::Kind<'k>>;
 
-/// Helper for working with a [`Holder`] using a closure.
-///
-/// This only works if `K` is `'static`.
-pub fn operate_in_closure<K: Opaque, F, R>(f: F) -> F 
-where F: for<'a> FnOnce(Pin<&'a <K as Opaque>::Kind<'a>>) -> R {
-    f
+    fn deref(&self) -> &Pin<&'k T::Kind<'k>> {
+        &self.inner
+    }
 }
 
 impl<'k, T> Holder<'k, T> where T: Opaque {
@@ -624,7 +602,6 @@ impl<'k, T> Holder<'k, T> where T: Opaque {
     /// use selfref::Holder;
     /// use selfref::new_with_closure;
     /// use selfref::opaque;
-    /// use selfref::operate_in_closure;
     ///
     /// #[derive(Default)]
     /// struct Foo<'a> {
@@ -644,9 +621,9 @@ impl<'k, T> Holder<'k, T> where T: Opaque {
     ///     ));
     ///     // Actually making our Foo refer to itself.
     ///     holder.as_ref().operate_in(
-    ///         operate_in_closure::<FooKey, _, _>(|foo| {
+    ///         |foo| {
     ///             foo.foo.set(Some(foo.get_ref()));
-    ///         })
+    ///         }
     ///     );
     /// }
     /// ```
@@ -663,7 +640,6 @@ impl<'k, T> Holder<'k, T> where T: Opaque {
     /// 
     /// use selfref::Holder;
     /// use selfref::NewWith;
-    /// use selfref::OperateIn;
     /// use selfref::opaque;
     ///
     /// struct Foo<'a, 'b: 'a> {
@@ -693,34 +669,32 @@ impl<'k, T> Holder<'k, T> where T: Opaque {
     ///     let stack_str = core::str::from_utf8(&stack_array).unwrap();
     ///     let holder = pin!(Holder::new_with(FooBuilder(stack_str)));
     ///     // Actually making our Foo refer to itself.
-    ///     struct SetFooRef;
-    ///     impl<'k, 'b: 'k> OperateIn<'k, FooKey<'b>> for SetFooRef {
-    ///         type Out = ();
-    ///         fn operate_in<'a>(self, foo: Pin<&'a Foo<'a, 'b>>)
-    ///         where 'k: 'a {
-    ///             foo.foo.set(Some(foo.get_ref()));
-    ///         }
-    ///     }
-    ///     holder.as_ref().operate_in(SetFooRef);
+    ///     holder.as_ref().operate_in(|foo| {
+    ///         foo.foo.set(Some(foo.get_ref()));
+    ///     });
     /// }
     /// ```
     pub fn operate_in<'i, F, R>(self: Pin<&'i Self>, f: F) -> R
     where 
-        F: OperateIn<'k, T, Out=R>
+        F: for<'x> FnOnce(OperateIn<'x, T>) -> R
     {
-        /// Converts Pin<&'a T::Kind<'k>> to Pin<&'b T::Kind<'b>>
+        /// Converts `Pin<&'a T::Kind<'k>>` to `Pin<&'b T::Kind<'b>>`.
+        ///
+        /// Not sure why this is called "upcast_dangling" since none of these
+        /// are actually dangling. But anyway.
         unsafe fn upcast_dangling<'a, 'b, 'c, T: Opaque + 'c>(
             x: Pin<&'a T::Kind<'c>>,
         ) -> Pin<&'b T::Kind<'b>>
         where T::Kind<'c>: 'a {
             mem::transmute(x)
         }
-
-        f.operate_in(unsafe {
-            upcast_dangling::<'i, 'k, '_, T>
-            (self.map_unchecked(|self_ref| {
-                &self_ref.inner
-            }))
+        f(OperateIn {
+            inner: unsafe {
+                upcast_dangling::<'i, 'k, '_, T>
+                (self.map_unchecked(|self_ref| {
+                    &self_ref.inner
+                }))
+            }
         })
     }
 }
diff --git a/tests/trybuild.rs b/tests/trybuild.rs
new file mode 100644
index 0000000..870c2f9
--- /dev/null
+++ b/tests/trybuild.rs
@@ -0,0 +1,5 @@
+#[test]
+fn ui() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("tests/ui/*.rs");
+}
diff --git a/tests/ui/example_need_cell.rs b/tests/ui/example_need_cell.rs
new file mode 100644
index 0000000..b6e12eb
--- /dev/null
+++ b/tests/ui/example_need_cell.rs
@@ -0,0 +1,12 @@
+//! Example of how Rust requires the use of a Cell to build a self-referential
+//! struct.
+//!
+//! NOTE: Also in top-level crate documentation. Keep 'em in sync.
+
+struct MySelfRefStruct<'this> {
+    this: &'this MySelfRefStruct<'this>,
+}
+
+fn main() {
+    let x = MySelfRefStruct { this: &x };
+}
diff --git a/tests/ui/example_need_cell.stderr b/tests/ui/example_need_cell.stderr
new file mode 100644
index 0000000..a01c9ae
--- /dev/null
+++ b/tests/ui/example_need_cell.stderr
@@ -0,0 +1,5 @@
+error[E0425]: cannot find value `x` in this scope
+  --> tests/ui/example_need_cell.rs:11:38
+   |
+11 |     let x = MySelfRefStruct { this: &x };
+   |                                      ^ not found in this scope
diff --git a/tests/ui/example_opaque_vs_drop.rs b/tests/ui/example_opaque_vs_drop.rs
new file mode 100644
index 0000000..b2c1809
--- /dev/null
+++ b/tests/ui/example_opaque_vs_drop.rs
@@ -0,0 +1,25 @@
+//! Example of how `opaque!` prevents unsound `Drop`.
+//!
+//! NOTE: Also in trait `Opaque` documentation. Keep 'em in sync.
+
+use std::cell::Cell;
+use selfref::opaque;
+
+struct Foo<'a> {
+    foo: Cell<Option<&'a Foo<'a>>>,
+}
+
+impl<'a> Drop for Foo<'a> {
+    fn drop(&mut self) {
+    }
+}
+
+struct FooKey;
+opaque! {
+    impl Opaque for FooKey {
+        type Kind<'a> = Foo<'a>;
+    }
+}
+
+fn main() {
+}
diff --git a/tests/ui/example_opaque_vs_drop.stderr b/tests/ui/example_opaque_vs_drop.stderr
new file mode 100644
index 0000000..1749031
--- /dev/null
+++ b/tests/ui/example_opaque_vs_drop.stderr
@@ -0,0 +1,15 @@
+error[E0597]: `x` does not live long enough
+  --> tests/ui/example_opaque_vs_drop.rs:18:1
+   |
+18 | / opaque! {
+19 | |     impl Opaque for FooKey {
+20 | |         type Kind<'a> = Foo<'a>;
+21 | |     }
+22 | | }
+   | | ^
+   | | |
+   | | borrowed value does not live long enough
+   | |_`x` dropped here while still borrowed
+   |   borrow might be used here, when `x` is dropped and runs the destructor for type `UBCheck<Foo<'_>>`
+   |
+   = note: this error originates in the macro `opaque` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/no_uaf_1.rs b/tests/ui/no_uaf_1.rs
new file mode 100644
index 0000000..e4d7ac4
--- /dev/null
+++ b/tests/ui/no_uaf_1.rs
@@ -0,0 +1,44 @@
+use core::cell::Cell;
+
+use selfref::Holder;
+use selfref::NewWith;
+use selfref::opaque;
+
+mod bad {
+    use super::*;
+    use std::rc::Rc;
+    struct Foo<'a> {
+        x: String,
+        y: Rc<Cell<&'a str>>,
+    }
+    struct FooOpaque;
+    opaque! {
+        impl Opaque for FooOpaque {
+            type Kind<'a> = Foo<'a>;
+        }
+    }
+    pub fn test() {
+        let rc = Rc::new(Cell::new(""));
+        struct FooBuilder<'b>(Rc<Cell<&'b str>>);
+        impl<'k, 'b> NewWith<'k, FooOpaque> for FooBuilder<'b> {
+            fn new_with<'a>(self) -> Foo<'a> where 'k: 'a {
+                Foo {
+                    x: "Hello".to_owned(),
+                    y: self.0,
+                }
+            }
+        }
+        let x = Holder::new_with(FooBuilder(rc.clone()));
+        let x = Box::pin(x);
+        x.as_ref().operate_in(|foo| {
+            let foo = foo.get_ref();
+            foo.y.set(&foo.x);
+        });
+        dbg!(&rc);
+        drop(x);
+        dbg!(&rc);
+    }
+}
+fn main() {
+    bad::test();
+}
diff --git a/tests/ui/no_uaf_1.stderr b/tests/ui/no_uaf_1.stderr
new file mode 100644
index 0000000..6db82ed
--- /dev/null
+++ b/tests/ui/no_uaf_1.stderr
@@ -0,0 +1,37 @@
+error: lifetime may not live long enough
+  --> tests/ui/no_uaf_1.rs:25:17
+   |
+23 |           impl<'k, 'b> NewWith<'k, FooOpaque> for FooBuilder<'b> {
+   |                    -- lifetime `'b` defined here
+24 |               fn new_with<'a>(self) -> Foo<'a> where 'k: 'a {
+   |                           -- lifetime `'a` defined here
+25 | /                 Foo {
+26 | |                     x: "Hello".to_owned(),
+27 | |                     y: self.0,
+28 | |                 }
+   | |_________________^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant
+   = note: the struct `Foo<'a>` is invariant over the parameter `'a`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: lifetime may not live long enough
+  --> tests/ui/no_uaf_1.rs:25:17
+   |
+23 |           impl<'k, 'b> NewWith<'k, FooOpaque> for FooBuilder<'b> {
+   |                    -- lifetime `'b` defined here
+24 |               fn new_with<'a>(self) -> Foo<'a> where 'k: 'a {
+   |                           -- lifetime `'a` defined here
+25 | /                 Foo {
+26 | |                     x: "Hello".to_owned(),
+27 | |                     y: self.0,
+28 | |                 }
+   | |_________________^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant
+   = note: the struct `Foo<'a>` is invariant over the parameter `'a`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+help: `'b` and `'a` must be the same: replace one with the other