Skip to content

field_ptr! creates unsound reference to uninitialized memory #2

New issue

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

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

Already on GitHub? # to your account

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?

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

No branches or pull requests

2 participants