Unsoundness in `Pin`

The implicated functions are Pin::as_mut (method 1) and Pin::clone (method 2), not Pin::as_ref.

The original PoC uses as_ref() (method 1) and as_mut() (method 2) to upcast to a trait object; you've shown that that's unnecessary, and you can rely on implicit coercion instead. But that's not the interesting part. dyn MyTrait does not impl Unpin, but you don't get any extra abilities from the type not being Unpin. It simply means that the type system no longer guarantees that is safe to unpin – but at that point the concrete type is still Wrapper, which actually is safe.

The dangerous part happens when it calls as_mut() for method 1 – a different call from the one you removed – or clone() for method 2. That's when the Wrapper is swapped out for the future.

Don't believe me? Well...

Here is a version that does the coercion before pinning. It goes from Box<Wrapper> to Box<dyn Dubious> to Pin<Box<dyn Dubious>> to Pin<&mut dyn Dubious>, so there's no coercion happening within the pin.

I'd like to make a version that doesn't use trait objects at all, but the lifetimes don't work out; having a type contain a mutable reference to itself makes the borrow checker act rather weird. I may have been wrong when I said trait objects weren't the only possibility.

6 Likes