Skip to content

PinCoerceUnsized can be bypassed using CoerceUnsized impl directly on the fundamental Pin #160756

Description

@maxdexh

Because Pin is fundamental, we can just bypass PinCoerceUnsized by doing impl CoerceUnsized for Pin<MySmartPtr>.

Here is an example that uses this to do an unsound Clone (playground):

#![forbid(unsafe_code)]
#![feature(unsize, coerce_unsized)]

use std::{
    any::{Any, type_name_of_val},
    marker::{PhantomPinned, Unsize},
    ops::{CoerceUnsized, Deref},
    pin::Pin,
    sync::Arc,
};

struct A<T: ?Sized>(Arc<T>);

impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<A<U>> for A<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Pin<A<U>>> for Pin<A<T>> {}
impl<T: ?Sized> Deref for A<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

trait Producer<T>: Any {
    fn get_clone(&self) -> A<dyn Producer<T>> {
        panic!("never called")
    }
    fn observe_pin(self: Pin<&Self>) {
        println!("Pinned {} at {self:p}", std::any::type_name::<Self>());
    }
}
impl<T: Any> Producer<T> for T {}
struct ValProducer<T> {
    ptr: Arc<T>,
}
impl<T> Unpin for ValProducer<T> {}
impl<T: 'static> Producer<T> for ValProducer<T> {
    fn get_clone(&self) -> A<dyn Producer<T>> {
        A(self.ptr.clone())
    }
}
impl<T: Any> Clone for A<dyn Producer<T>> {
    fn clone(&self) -> Self {
        (**self).get_clone()
    }
}

fn break_drop_guarantee<T: Any>(ptr: Arc<T>) {
    let unpin: Pin<A<ValProducer<T>>> = Pin::new(A(Arc::new(ValProducer { ptr: ptr.clone() })));
    let unpin: Pin<A<dyn Producer<T>>> = unpin;
    let pinned: Pin<A<dyn Producer<T>>> = unpin.clone();
    pinned.as_ref().observe_pin();
    drop((unpin, pinned));

    println!("Observing unpinned {} at {ptr:p}", type_name_of_val(&ptr));

    let Ok(_unwrapped) = Arc::try_unwrap(ptr) else {
        unreachable!()
    };
}

fn main() {
    struct Victim {
        _data: [u8; 32],
        _p: PhantomPinned,
    }
    break_drop_guarantee(Arc::new(Victim {
        _data: [0; _],
        _p: PhantomPinned,
    }));
    impl Drop for Victim {
        fn drop(&mut self) {
            println!("Dropping at {self:p}");
        }
    }
}

This should also affect #156935.

@rustbot label A-pin F-coerce_unsized requires-nightly I-unsound

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-pinArea: PinC-bugCategory: This is a bug.F-coerce_unsizedThe `CoerceUnsized` traitI-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundnessrequires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions