How to use `addr_of!` to create an unaligned pointer?

The addr_of! docs imply that it can handle unaligned pointers so long as a reference is never materialized from that pointer (which would be insta-UB). However, running the following program under Miri fails:

fn main() {
    #[repr(C, packed)]
    struct Unalign<T>(T);
    struct Foo {
        a: u8,
        b: u16,
    // Has alignment `align_of::<T>()`, and the `Unalign<T>`
    // is at byte offset 1; so long as `align_of::<T>() > 1`,
    // the contained `T` is misaligned.
    struct Misalign<T>(u8, Unalign<T>, [T; 0]);
    let u = Misalign(0, Unalign(Foo{ a: 1, b: 2 }), []);
    let u_ptr: *const Unalign<Foo> = &u.1;
    // Sound because `Unalign` contains a `T` and nothing else.
    let f_ptr: *const Foo = u_ptr.cast();
    // Should be sound because we never construct a reference.
    let addr_of_b: *const u16 = unsafe { core::ptr::addr_of!((*f_ptr).b) };
    println!("{:?}", addr_of_b);

Here's the failure:

error: Undefined Behavior: accessing memory with alignment 1, but alignment 2 is required
  --> src/
22 |     let addr_of_b: *const u16 = unsafe { core::ptr::addr_of!((*f_ptr).b) };
   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment 1, but alignment 2 is required
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see for further information
   = note: BACKTRACE:
   = note: inside `main` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/ 1980:22
   = note: this error originates in the macro `core::ptr::addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)

Am I misunderstanding addr_of!'s requirements? Is there any way to get this to be sound?

Context: Support field projection in any `#[repr(transparent)]` wrapper type · Issue #196 · google/zerocopy · GitHub

The example shows an unaligned field (since it's packed), but that doesn't excuse everything. "Note, however, that the expr in addr_of!(expr) is still subject to all the usual rules." -- I think this would include alignment for (*f_ptr), although the docs only call out null pointers.

Miri is happy with addr_of!((*u_ptr).0.b).

That makes sense, thanks!

For those following along: I submitted a rust-lang/rust issue to hopefully improve the docs.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.