Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

field_ptr! creates unsound reference to uninitialized memory #2

Open
stevenengler opened this issue Jul 17, 2023 · 1 comment
Open

Comments

@stevenengler
Copy link

stevenengler commented Jul 17, 2023

The field_ptr macro

macro_rules! field_ptr {
    ($base_ptr:ident.$field:ident) => {
        // The only valid pointer cast from &T is *const T, so this is always
        // a const pointer to the field type exactly.
        &(*$base_ptr).$field as *const _
    };
}

creates a temporary reference to the structure but offset_of doesn't initialize the structure, so this creates a temporary reference to uninitialized memory which is unsound. There's related discussion in rust-lang/rust-bindgen#1651.

This also means that this macro can't be used to get the offset of a field in a packed struct:

#[repr(packed)]
struct Foo {
    x: u8,
    y: u64,
}

fn main() {
    let foo: MaybeUninit<Foo> = MaybeUninit::uninit();
    let base_ptr = foo.as_ptr();
    let _ptr = unsafe { field_ptr!(base_ptr.y) };
}
error[E0793]: reference to packed field is unaligned
  --> src/main.rs:7:9
   |
7  |         &(*$base_ptr).$field as *const _
   |         ^^^^^^^^^^^^^^^^^^^^
...
21 |     let _ptr = unsafe { field_ptr!(base_ptr.y) };
   |                         ---------------------- in this macro invocation

The proper way to get a field offset of an uninitialized struct is with the std::ptr::addr_of macro.

Create a const raw pointer to a place, without creating an intermediate reference.

Creating a reference with &/&mut is only allowed if the pointer is properly aligned and points to initialized data. For cases where those requirements do not hold, raw pointers should be used instead. However, &expr as *const _ creates a reference before casting it to a raw pointer, and that reference is subject to the same rules as all other references. This macro can create a raw pointer without creating a reference first.

nomicon:

Note that, to use the ptr methods, you need to first obtain a raw pointer to the data you want to initialize. It is illegal to construct a reference to uninitialized data, which implies that you have to be careful when obtaining said raw pointer:

  • [...]
  • For a struct, however, in general we do not know how it is laid out, and we also cannot use &mut base_ptr.field as that would be creating a reference. So, you must carefully use the addr_of_mut macro. This creates a raw pointer to the field without creating an intermediate reference
@nox
Copy link
Owner

nox commented Jul 18, 2023

Yeah I know, but addr_of wasn't stable yet back then. Care to make a PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants