Skip to content

Commit 883b6aa

Browse files
committed
Auto merge of #67039 - xfix:manually-implement-pin-traits, r=nikomatsakis
Use deref target in Pin trait implementations Using deref target instead of pointer itself avoids providing access to `&Rc<T>` for malicious implementations, which would allow calling `Rc::get_mut`. This is a breaking change necessary due to unsoundness, however the impact of it should be minimal. This only fixes the issue with malicious `PartialEq` implementations, other `Pin` soundness issues are still here. See <https://internals.rust-lang.org/t/unsoundness-in-pin/11311/73> for more details.
2 parents 975e83a + 61d9c00 commit 883b6aa

File tree

3 files changed

+81
-17
lines changed

3 files changed

+81
-17
lines changed

src/libcore/pin.rs

+41-17
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@
376376

377377
use crate::cmp::{self, PartialEq, PartialOrd};
378378
use crate::fmt;
379+
use crate::hash::{Hash, Hasher};
379380
use crate::marker::{Sized, Unpin};
380381
use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver};
381382

@@ -390,55 +391,78 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver};
390391
/// [`Unpin`]: ../../std/marker/trait.Unpin.html
391392
/// [`pin` module]: ../../std/pin/index.html
392393
//
393-
// Note: the derives below, and the explicit `PartialEq` and `PartialOrd`
394-
// implementations, are allowed because they all only use `&P`, so they cannot move
395-
// the value behind `pointer`.
394+
// Note: the `Clone` derive below causes unsoundness as it's possible to implement
395+
// `Clone` for mutable references.
396+
// See <https://internals.rust-lang.org/t/unsoundness-in-pin/11311> for more details.
396397
#[stable(feature = "pin", since = "1.33.0")]
397398
#[lang = "pin"]
398399
#[fundamental]
399400
#[repr(transparent)]
400-
#[derive(Copy, Clone, Hash, Eq, Ord)]
401+
#[derive(Copy, Clone)]
401402
pub struct Pin<P> {
402403
pointer: P,
403404
}
404405

405-
#[stable(feature = "pin_partialeq_partialord_impl_applicability", since = "1.34.0")]
406-
impl<P, Q> PartialEq<Pin<Q>> for Pin<P>
406+
// The following implementations aren't derived in order to avoid soundness
407+
// issues. `&self.pointer` should not be accessible to untrusted trait
408+
// implementations.
409+
//
410+
// See <https://internals.rust-lang.org/t/unsoundness-in-pin/11311/73> for more details.
411+
412+
#[stable(feature = "pin_trait_impls", since = "1.41.0")]
413+
impl<P: Deref, Q: Deref> PartialEq<Pin<Q>> for Pin<P>
407414
where
408-
P: PartialEq<Q>,
415+
P::Target: PartialEq<Q::Target>,
409416
{
410417
fn eq(&self, other: &Pin<Q>) -> bool {
411-
self.pointer == other.pointer
418+
P::Target::eq(self, other)
412419
}
413420

414421
fn ne(&self, other: &Pin<Q>) -> bool {
415-
self.pointer != other.pointer
422+
P::Target::ne(self, other)
416423
}
417424
}
418425

419-
#[stable(feature = "pin_partialeq_partialord_impl_applicability", since = "1.34.0")]
420-
impl<P, Q> PartialOrd<Pin<Q>> for Pin<P>
426+
#[stable(feature = "pin_trait_impls", since = "1.41.0")]
427+
impl<P: Deref<Target: Eq>> Eq for Pin<P> {}
428+
429+
#[stable(feature = "pin_trait_impls", since = "1.41.0")]
430+
impl<P: Deref, Q: Deref> PartialOrd<Pin<Q>> for Pin<P>
421431
where
422-
P: PartialOrd<Q>,
432+
P::Target: PartialOrd<Q::Target>,
423433
{
424434
fn partial_cmp(&self, other: &Pin<Q>) -> Option<cmp::Ordering> {
425-
self.pointer.partial_cmp(&other.pointer)
435+
P::Target::partial_cmp(self, other)
426436
}
427437

428438
fn lt(&self, other: &Pin<Q>) -> bool {
429-
self.pointer < other.pointer
439+
P::Target::lt(self, other)
430440
}
431441

432442
fn le(&self, other: &Pin<Q>) -> bool {
433-
self.pointer <= other.pointer
443+
P::Target::le(self, other)
434444
}
435445

436446
fn gt(&self, other: &Pin<Q>) -> bool {
437-
self.pointer > other.pointer
447+
P::Target::gt(self, other)
438448
}
439449

440450
fn ge(&self, other: &Pin<Q>) -> bool {
441-
self.pointer >= other.pointer
451+
P::Target::ge(self, other)
452+
}
453+
}
454+
455+
#[stable(feature = "pin_trait_impls", since = "1.41.0")]
456+
impl<P: Deref<Target: Ord>> Ord for Pin<P> {
457+
fn cmp(&self, other: &Self) -> cmp::Ordering {
458+
P::Target::cmp(self, other)
459+
}
460+
}
461+
462+
#[stable(feature = "pin_trait_impls", since = "1.41.0")]
463+
impl<P: Deref<Target: Hash>> Hash for Pin<P> {
464+
fn hash<H: Hasher>(&self, state: &mut H) {
465+
P::Target::hash(self, state);
442466
}
443467
}
444468

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Pin's PartialEq implementation allowed to access the pointer allowing for
2+
// unsoundness by using Rc::get_mut to move value within Rc.
3+
// See https://internals.rust-lang.org/t/unsoundness-in-pin/11311/73 for more details.
4+
5+
use std::ops::Deref;
6+
use std::pin::Pin;
7+
use std::rc::Rc;
8+
9+
struct Apple;
10+
11+
impl Deref for Apple {
12+
type Target = Apple;
13+
fn deref(&self) -> &Apple {
14+
&Apple
15+
}
16+
}
17+
18+
impl PartialEq<Rc<Apple>> for Apple {
19+
fn eq(&self, _rc: &Rc<Apple>) -> bool {
20+
unreachable!()
21+
}
22+
}
23+
24+
fn main() {
25+
let _ = Pin::new(Apple) == Rc::pin(Apple);
26+
//~^ ERROR type mismatch resolving
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0271]: type mismatch resolving `<std::rc::Rc<Apple> as std::ops::Deref>::Target == std::rc::Rc<Apple>`
2+
--> $DIR/issue-67039-unsound-pin-partialeq.rs:25:29
3+
|
4+
LL | let _ = Pin::new(Apple) == Rc::pin(Apple);
5+
| ^^ expected struct `Apple`, found struct `std::rc::Rc`
6+
|
7+
= note: expected type `Apple`
8+
found struct `std::rc::Rc<Apple>`
9+
= note: required because of the requirements on the impl of `std::cmp::PartialEq<std::pin::Pin<std::rc::Rc<Apple>>>` for `std::pin::Pin<Apple>`
10+
11+
error: aborting due to previous error
12+
13+
For more information about this error, try `rustc --explain E0271`.

0 commit comments

Comments
 (0)