Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StableDeref is unsound for &T #10

Closed
KamilaBorowska opened this issue Nov 19, 2019 · 5 comments
Closed

StableDeref is unsound for &T #10

KamilaBorowska opened this issue Nov 19, 2019 · 5 comments

Comments

@KamilaBorowska
Copy link

KamilaBorowska commented Nov 19, 2019

The following code violates StableDeref requirements.

use stable_deref_trait::StableDeref;
use std::cell::Cell;
use std::ops::DerefMut;
use std::sync::atomic::{AtomicUsize, Ordering};

static POSITION: AtomicUsize = AtomicUsize::new(0);

struct MyEvilStruct<'a>([Cell<Option<&'a mut MyEvilStruct<'a>>>; 2]);

impl<'a> DerefMut for &MyEvilStruct<'a> {
    fn deref_mut(&mut self) -> &mut MyEvilStruct<'a> {
        self.0[POSITION.fetch_add(1, Ordering::Relaxed)].take().unwrap()
    }
}

fn assert_deref_mut_sane<T: StableDeref + DerefMut>(mut a: T) {
    let p: *mut T::Target = &mut *a;
    let q: *mut T::Target = &mut *a;
    assert_eq!(p, q);
}

fn main() {
    let mut a = MyEvilStruct([Cell::new(None), Cell::new(None)]);
    let mut b = MyEvilStruct([Cell::new(None), Cell::new(None)]);
    let p = MyEvilStruct([Cell::new(Some(&mut a)), Cell::new(Some(&mut b))]);
    assert_deref_mut_sane(&p);
}
@Storyyeller
Copy link
Owner

It sounds like you can supply a custom DerefMut impl for &T. Is that correct?

@KamilaBorowska
Copy link
Author

Yes.

@Storyyeller
Copy link
Owner

Storyyeller commented Nov 21, 2019

That's unfortunate. What do you suggest? It seems like a fundamental assumption of owning_ref style crates is being violated, but there's no good way to fix it. Of course, there's already unsoundness due to noalias, but this might be easier to trigger.

@KamilaBorowska
Copy link
Author

KamilaBorowska commented Nov 21, 2019

Honestly, I have no idea. There are two ways to go about it, but they are all to certain extent breaking changes.

  1. Introduce new StableDerefMut trait that extends the StableDeref guarantee to DerefMut.
  2. Remove impl<'a, T: ?Sized> StableDeref for &'a T. All other implementations seem fine. It's possible to introduce a newtype for references that is safe, unlike &T.

And there is a third way which isn't strictly speaking breaking, but is more complicated:

  1. Get the compiler to prevent DerefMut implementations for &T. This would involve change in the language rather than this crate. Isn't too unlikely, as std::pin::Pin is affected by the same issue.

Waiting for standard library to fix the issue in std::pin::Pin may be acceptable as this issue affects standard library as well. While it is a "breaking change" according to RFC 1105 (&T is a fundamental type), I hope Rust developers will do it, because come on, who seriously implements DerefMut for shared references.

@Storyyeller
Copy link
Owner

Storyyeller commented Nov 21, 2019

I'll hope for the third option for now.

Option one or two risk breaking downstream users of owning_ref, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants