owned this note
owned this note
Published
Linked with GitHub
# Valid uses of addr_of!
The documentation for `addr_of!` says this:
> Note, however, that the `expr` in `addr_of!(expr)` is still subject to all the usual rules. In particular, `addr_of!(*ptr::null())` is Undefined Behavior because it dereferences a null pointer.
This wording is quite unclear about what "the usual rules" are. The community wants this use of `addr_of!` to be valid, as a technique to turn an unaligned pointer to a struct into an unaligned pointer to one of its fields:
(Ignore the fact that the struct isn't at some funky alignment so this code isn't quite a perfect example)
```rust
struct Misalignment {
a: u32,
}
fn main() {
let items: [Misalignment; 2] = [Misalignment { a: 0 }, Misalignment { a: 1 }];
unsafe {
let ptr: *const Misalignment = items.as_ptr().cast::<u8>().add(1).cast::<Misalignment>();
let _ptr = core::ptr::addr_of!((*ptr).a);
}
}
```
The same "usual rules" argument that says that disallowing dereference of a null pointer can reasonably lead a user to conclude that `addr_of!` use like above is not valid because it dereferences a misaligned pointer. But if the above code is not allowed, `addr_of!` is not very useful.
## Other possibly-valid calls
Is this well-defined?
```rust=
#[repr(packed)]
struct Misaligner {
_head: u8,
tail: u32,
}
fn main() {
let oops = Misaligner {
_head: 0u8,
tail: 0u32,
};
let ptr: *const Misaligner = &oops as *const Misaligner;
unsafe {
let _ptr: *const u32 = core::ptr::addr_of!((*ptr).tail);
}
}
```
## Deref coercion
Some users want `addr_of!` to not go through Deref coercion. The ambigiuty of combining "the usual rules" along with probably permitting the unaligned dereference above makes it unclear if `addr_of!` is somehow special. Whatever is resolved here should make it clear whether or not Deref coercion applies inside `addr_of!`.
## Operations in order
* Deref: Value -> Place
* Checks: Inbounds, alignment
* Offset: Place -> Place
* Checks: Off-topic for this meeting, discussed in previous meeting
* `addr_of!`: Place -> Value
* Checks: Nothing
## Examples for discussion
```rust=
#[repr(align(8))]
struct Overaligned(u32);
let o = [0_u32; 2]; // Assume allocated at alignment 4, consider if size 1
let p = addr_of!(o) as *const Overaligned;
let _x = (*p).0; // UB under Ralf's alignment computation, but could be allowed
```
If people expect the first example to be allowed (alignment violation),
then wouldn't they also expect this to be allowed? Should we allow this?
```rust=
#[repr(C)]
struct Misalignment {
x: u32,
a: u32,
}
fn main() {
unsafe {
let ptr: *const Misalignment = 1usize as *const Misalignment;
let _ptr = core::ptr::addr_of!((*ptr).a);
}
// many people imagine this happens
unsafe {
let ptr: *const Misalignment = 1usize as *const Misalignment;
let offset: usize = offset_of!(Misalignment.a);
let out = ptr.wrapping_bytes_add(offset);
}
// ... but it's actually `bytes_add`, no `wrapping`!
}
```
```rust
struct S {
a: (),
b: u32,
}
let r: &S = ...;
foo(); // deallocs memory backing `r`
let _x = (*r).a;
```
## We should write down the rules and PR to `addr_of`/`addr_of_mut`
\- Connor
We should come up with some set of rules for place projections/pointer derefence, and PR them to the docs. They can be conservative or lax, probably more conservative for now since they can be relaxed later.
Should we document relaxation rules:
- Alignment probably yes?
- Dereferenceable?