Skip to content

as_ptr()'s use of *mut is misleading #24

Open

Description

@kornelski

as_ptr() returns a *mut _ pointer. This isn't an error by itself, but under Rust's memory model, this pointer is derived from shared/immutable &self, and therefore doesn't have permission to mutate self, so the mut of the pointer is misleading.

fn as_ptr(&self) -> *mut Self::CType {

I assume this is for convenience, because C APIs aren't diligent about const vs mut distinction. However, this is a gotcha, because C APIs that take *mut may actually mutate the object, and that is UB from Rust's perspective.

Could you add .as_mut_ptr() that takes &mut self to provide a pointer safe for mutation?

Activity

  1. sfackler commented on Nov 28, 2023

    @sfackler
    Owner

    The structure of Opaque is designed to prevent mutation through the *mut from being UB:

    pub struct Opaque(PhantomData<UnsafeCell<*mut ()>>);
    .

    Even for C APIs that are const-correct, C's notion of constness is distinct enough from Rust's that & <-> *const and &mut <-> *mut doesn't really work.

  2. icmccorm commented on Nov 30, 2023

    @icmccorm

    I've developed a version of Miri that can execute foreign functions by interpreting LLVM bytecode, and it found a Tree Borrows violation related to mutating through self.as_ptr(). Writing through the mutable pointer created from self.as_ptr() is considered undefined behavior under this model, since this pointer is given a Frozen access tag as a child tag of the implicit &self parameter to as_ptr.

    Here's a minimal example that can recreate the error with an unmodified version of Miri, without the FFI. In particular, this error is triggered when mutating through self.as_ptr() within a method that receives &mut self, where self is an instance of ForeignTypeRef.

    impl ExampleRef {
        fn invalid_mutation(&mut self) {
            let raw_ptr: *mut ExampleInner = self.as_ptr();
            unsafe {
                (*raw_ptr).inner = 1;
            }
        }
    }
    

    Also, I was a bit unsure about what you meant here:

    C's notion of constness is distinct enough from Rust's that & <-> *const and &mut <-> *mut doesn't really work.

    Are you saying that the compiler's treatment of &T differs enough from * const in C (even under a const-correct API) that this borrowing violation isn't actually problematic in practice?

  3. sfackler commented on Nov 30, 2023

    @sfackler
    Owner

    The &self is a pointer to UnsafeCell (semantically through a PhantomData). Why is it being tagged as Frozen?

  4. kornelski commented on Nov 30, 2023

    @kornelski
    ContributorAuthor

    Could it be because UnsafeCell in PhantomData has zero size, and doesn't exist in memory? If it was Opaque(UnsafeCell<ActualForeignSizedType>) then it would map to an address space.

  5. madsmtm commented on Nov 30, 2023

    @madsmtm

    Yeah, I think there's a difference between PhantomData<UnsafeCell<*mut ()>> and UnsafeCell<PhantomData<*mut ()>>, with only the latter registering as using interior mutability

  6. added a commit that references this issue on Jan 11, 2024
    fcc9289
  7. madsmtm commented on Jan 16, 2024

    @madsmtm

    I'll note again: rustc currently treats UnsafeCell differently whether it's inside PhantomData or not! See this playground, click "Show LLVM IR", and inspect the result; the function taking &PhantomData<UnsafeCell<*mut ()>> gets marked with noalias readonly, while &UnsafeCell<PhantomData<*mut ()>> gets marked with readnone.

    If you add extern types to the mix, you'll see the exact same LLVM IR as for &UnsafeCell<PhantomData<*mut ()>> (LLVM will even merge the functions because of that).

  8. madsmtm commented on Jan 16, 2024

    @madsmtm

    See also discussion from rust-lang/unsafe-code-guidelines#236 (comment), this is still not determined

  9. icmccorm commented on Jan 16, 2024

    @icmccorm

    If you add extern types to the mix, you'll see the exact same LLVM IR

    @madsmtm I think you accidentally used the same link twice.

    Based on this pull request, allowing PhantomData to apply interior mutability would be a breaking change, so it's not likely to be resolved in this direction soon. It seems like it would be worthwhile to change Opaque for now until the semantics are settled.

  10. madsmtm commented on Jan 16, 2024

    @madsmtm

    I think you accidentally used the same link twice.

    Apologies, I've fixed it now.

  11. taikulawo commented on Jan 20, 2025

    @taikulawo

    From my view, Opaque is zero size type,
    So SslRef should be ZST

    pub struct SslRef(foreign_types::Opaque);
    

    Does we convert pointer(for example, *mut ffi::SSL) to SslRef is soundness?

    #[inline]
    unsafe fn from_ptr_mut<'a>(ptr: *mut Self::CType) -> &'a mut Self {
        &mut *(ptr as *mut _)
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions