Provenance of pointers encoded/decoded to byte arrays `std::ptr::read()`/`std::ptr::write()` across the C FFI

⚓ Rust    📅 2026-03-14    👤 surdeus    👁️ 6      

surdeus

I've submitted this on SO as well but maybe I'll be more lucky here.

I think I've kind of understood provenance (maybe not), but all the documentation I find talks about going from/to pointers and usize. But what about [u8; N] ? There's nothing in the documentation about what happens to pointers encoded/decoded to byte arrays using std::ptr::read()/std::ptr::write().

Specifically, suppose I have this code:

#[repr(C)]
struct MyStruct {
    pub ptr: *const u32
}

#[repr(C)]
struct RawData {
    pub data: [u8; std::mem::size_of::<MyStruct>()]
}

unsafe extern "C" {
    pub fn foreign() -> RawData;
}

pub fn does_this_work() -> MyStruct {
    unsafe {
        let data = foreign();
    
        std::ptr::read_unaligned((&raw data) as *const MyStruct)
    }
}

Why I'm doing that is beyond this question, but this is somewhat similar to what the Zngur C++ interop tool does under the hood.

From what I've understood about provenance, if the C API was returning a uintptr_t, aka usize, I would need to ensure the pointer obtained by it has provenance (using std::ptr::with_exposed_provenance() or std::ptr::with_addr(). But here there is no such usize.

So what I cannot understand is the following:

  1. does the raw pointer inside the resulting MyStruct have provenance?

  2. is it valid to dereference (assuming it was on the C side)?

  3. if not, why?

  4. what to do to fix the situation?

Another way to put this is: is what Zngur is doing sound?

1 post - 1 participant

Read full topic

🏷️ Rust_feed