Provenance of pointers encoded/decoded to byte arrays `std::ptr::read()`/`std::ptr::write()` across the C FFI
⚓ Rust 📅 2026-03-14 👤 surdeus 👁️ 6I'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:
-
does the raw pointer inside the resulting
MyStructhave provenance? -
is it valid to dereference (assuming it was on the C side)?
-
if not, why?
-
what to do to fix the situation?
Another way to put this is: is what Zngur is doing sound?
1 post - 1 participant
🏷️ Rust_feed