diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index 88fa718ae9e62..6a0c5bbebc1c6 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -376,6 +376,7 @@ use crate::cmp::{self, PartialEq, PartialOrd}; use crate::fmt; +use crate::hash::{Hash, Hasher}; use crate::marker::{Sized, Unpin}; use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; @@ -390,55 +391,78 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; /// [`Unpin`]: ../../std/marker/trait.Unpin.html /// [`pin` module]: ../../std/pin/index.html // -// Note: the derives below, and the explicit `PartialEq` and `PartialOrd` -// implementations, are allowed because they all only use `&P`, so they cannot move -// the value behind `pointer`. +// Note: the `Clone` derive below causes unsoundness as it's possible to implement +// `Clone` for mutable references. +// See for more details. #[stable(feature = "pin", since = "1.33.0")] #[lang = "pin"] #[fundamental] #[repr(transparent)] -#[derive(Copy, Clone, Hash, Eq, Ord)] +#[derive(Copy, Clone)] pub struct Pin

{ pointer: P, } -#[stable(feature = "pin_partialeq_partialord_impl_applicability", since = "1.34.0")] -impl PartialEq> for Pin

+// The following implementations aren't derived in order to avoid soundness +// issues. `&self.pointer` should not be accessible to untrusted trait +// implementations. +// +// See for more details. + +#[stable(feature = "pin_trait_impls", since = "1.41.0")] +impl PartialEq> for Pin

where - P: PartialEq, + P::Target: PartialEq, { fn eq(&self, other: &Pin) -> bool { - self.pointer == other.pointer + P::Target::eq(self, other) } fn ne(&self, other: &Pin) -> bool { - self.pointer != other.pointer + P::Target::ne(self, other) } } -#[stable(feature = "pin_partialeq_partialord_impl_applicability", since = "1.34.0")] -impl PartialOrd> for Pin

+#[stable(feature = "pin_trait_impls", since = "1.41.0")] +impl> Eq for Pin

{} + +#[stable(feature = "pin_trait_impls", since = "1.41.0")] +impl PartialOrd> for Pin

where - P: PartialOrd, + P::Target: PartialOrd, { fn partial_cmp(&self, other: &Pin) -> Option { - self.pointer.partial_cmp(&other.pointer) + P::Target::partial_cmp(self, other) } fn lt(&self, other: &Pin) -> bool { - self.pointer < other.pointer + P::Target::lt(self, other) } fn le(&self, other: &Pin) -> bool { - self.pointer <= other.pointer + P::Target::le(self, other) } fn gt(&self, other: &Pin) -> bool { - self.pointer > other.pointer + P::Target::gt(self, other) } fn ge(&self, other: &Pin) -> bool { - self.pointer >= other.pointer + P::Target::ge(self, other) + } +} + +#[stable(feature = "pin_trait_impls", since = "1.41.0")] +impl> Ord for Pin

{ + fn cmp(&self, other: &Self) -> cmp::Ordering { + P::Target::cmp(self, other) + } +} + +#[stable(feature = "pin_trait_impls", since = "1.41.0")] +impl> Hash for Pin

{ + fn hash(&self, state: &mut H) { + P::Target::hash(self, state); } } diff --git a/src/test/ui/issues/issue-67039-unsound-pin-partialeq.rs b/src/test/ui/issues/issue-67039-unsound-pin-partialeq.rs new file mode 100644 index 0000000000000..a496e58a79bdd --- /dev/null +++ b/src/test/ui/issues/issue-67039-unsound-pin-partialeq.rs @@ -0,0 +1,27 @@ +// Pin's PartialEq implementation allowed to access the pointer allowing for +// unsoundness by using Rc::get_mut to move value within Rc. +// See https://internals.rust-lang.org/t/unsoundness-in-pin/11311/73 for more details. + +use std::ops::Deref; +use std::pin::Pin; +use std::rc::Rc; + +struct Apple; + +impl Deref for Apple { + type Target = Apple; + fn deref(&self) -> &Apple { + &Apple + } +} + +impl PartialEq> for Apple { + fn eq(&self, _rc: &Rc) -> bool { + unreachable!() + } +} + +fn main() { + let _ = Pin::new(Apple) == Rc::pin(Apple); + //~^ ERROR type mismatch resolving +} diff --git a/src/test/ui/issues/issue-67039-unsound-pin-partialeq.stderr b/src/test/ui/issues/issue-67039-unsound-pin-partialeq.stderr new file mode 100644 index 0000000000000..3330d60242f1b --- /dev/null +++ b/src/test/ui/issues/issue-67039-unsound-pin-partialeq.stderr @@ -0,0 +1,13 @@ +error[E0271]: type mismatch resolving ` as std::ops::Deref>::Target == std::rc::Rc` + --> $DIR/issue-67039-unsound-pin-partialeq.rs:25:29 + | +LL | let _ = Pin::new(Apple) == Rc::pin(Apple); + | ^^ expected struct `Apple`, found struct `std::rc::Rc` + | + = note: expected type `Apple` + found struct `std::rc::Rc` + = note: required because of the requirements on the impl of `std::cmp::PartialEq>>` for `std::pin::Pin` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`.