Consider following simple rust program
#[repr(packed)]
pub struct TwuMsg {
pub id: u64,
}
fn main() {
let msg = TwuMsg { id: 1 };
println!("msg.id {}", msg.id);
}
It fails to compile. rustc suggests following help
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
But if I change main according to note, It works fine.
fn main() {
let msg = TwuMsg { id: 1 };
// isn't msg.id is also accessing unaligned field too? Why it's considered safe?
let id = msg.id;
println!("msg.id {}", id);
}
I tried to get answers from different sources. They claimed that rust compiler ensures the safety. Then my counter would be
why rustc can't ensure safety for direct reference to field if it can for copy operation?
If you can explain the diff in terms of CPU and instructions generated that would be deeply appreciated.
[UPDATE]
I've gone through official reference already at https://doc.rust-lang.org/reference/behavior-considered-undefined.html#places-based-on-misaligned-pointers but I'm more interested to know about the motive behind this design decision. Usually if data is unaligned CPU will spend additional cycle for the read. Is performance penalty is the only reason behind considering this a UB?
This is due to the memory allocation for rust's secure access system. First when you use
#[repr(packed)]rustwill sort the fields with minimal alignment leading to some fields that can never be aligned (this is due to the way memory is allocated Rust hardware does not determine it). unaligned access and field leads to uncomputable behavior (it's easy to hack like ios m8 bug)in the first code you try to directly access
msg.idof typeu64 8bytefrom an address that may not be8 bytethis is dangerous behaviorrustcwill ban itIn the second code you are doing a read of
let id = msg.idyou are copying toidwhich is safe because it will copy the bytes frommsg.idtoidwithout caring about the alignment of the result isidaligned to8 bytesbecause rust always ensures correct local variable alignmentimportant here rust ensures compile safe copying as it involves moving bytes but it cannot guarantee safe reference to fields as it involves unsafe memory access
Easy explanation
[repr(packed)]rust will not provide anypadding. for example ifidonly has6 bytesthen rust will give it a whole row ofbytesbecause there is nothing2 bytesto put in that place soidcan instead be accessed Its6 bytes' have the ability to access another2 bytes' next to it in the memory bar. If you don't want to uselet id = msg.idyou can#[repr(C)]this mode will add padding to fill the memory to prevent unauthorized access.