Accessing a Pin's pointer type

I'm working on a project involving C FFI where the C side has to store a pointer to a struct created and owned by the Rust side. For this case I ended up using Pin<Arc<T>>, which seems to fit the bill quite well.

However, it seems that once pinned there is no way to access the Arc itself, which I need for a few methods it provides (such as Arc::get_mut and Arc::ptr_eq).

This has led me to create these few utility functions:

fn pin_get_ptr<P>(pin: &Pin<P>) -> &P {
    // SAFETY: Pin is repr(transparent), and we are not moving anything
    unsafe { &*(pin as *const _ as *const P) }
}

unsafe fn pin_get_ptr_unchecked_mut<P>(pin: &mut Pin<P>) -> &mut P {
    // SAFETY: Pin is repr(transparent). The caller is responsible to ensure the data will never be
    // moved, similar to Pin::get_unchecked_mut
    unsafe { &mut *(pin as *mut _ as *mut P) }
}

fn arc_get_pin_mut<T>(pin: &mut Pin<Arc<T>>) -> Option<Pin<&mut T>> {
    // SAFETY: Arc::get_mut does not move anything
    let arc = unsafe { pin_get_ptr_unchecked_mut(pin) };
    let inner = Arc::get_mut(arc)?;

    // SAFETY: By using get_mut we guaranteed this is the only reference to it.
    // The &mut Pin<Arc<T>> argument guarantees this data was pinned, and the temporary Arc reference
    // is never exposed.
    unsafe { Some(Pin::new_unchecked(inner)) }
}

My main question is: are those functions sound? They seem sound to me, and Miri doesn't seem to complain (although I don't think it knows about pinning guarantees at all), but since exposing the inner pointer of a Pin is not something that I see done very often, I would like some additional opinions on this.

Assuming this is sound, I am also wondering if this functionality would be desirable to have in the standard library directly. I'm not sure if being unable to access the pointer type itself is a design decision, or simply a case of "there hasn't been much demand yet"?

Thanks in advance! :slightly_smiling_face:

It's unsound. You must not be able to get Arc<T> to the same T value in safe Rust after the Pin<Arc<T>> is constructed. Here's the UB case.

let pin_arc = Arc::pin(not_unpin());
let mut arc = Arc::clone(pin_get_ptr(&pin_arc));
drop(pin_arc);
// !!!UB!!!
let pinned_value_moved = std::mem::replace(
    Arc::get_mut(&mut arc).unwrap(),
    not_unpin(),
);

I think adding it as an unsafe fn to the stdlib is ok. The Pin type already contains many associated unsafe functions.

3 Likes

Ah, yes you're right, good catch! I've updated the code in my project and made pin_get_ptr unsafe (hence leaving the responsibility of not cloning the pointer to the caller), thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.