Add: windows mvp - transparent bugs not fixed
This commit is contained in:
142
skills/unsafe-checker/rules/ffi-11-packed-ub.md
Normal file
142
skills/unsafe-checker/rules/ffi-11-packed-ub.md
Normal file
@@ -0,0 +1,142 @@
|
||||
---
|
||||
id: ffi-11
|
||||
original_id: P.UNS.FFI.11
|
||||
level: P
|
||||
impact: HIGH
|
||||
clippy: unaligned_references
|
||||
---
|
||||
|
||||
# Be Careful with UB When Referencing #[repr(packed)] Struct Fields
|
||||
|
||||
## Summary
|
||||
|
||||
Creating references to fields in `#[repr(packed)]` structs is undefined behavior if the field is misaligned. Use raw pointers and `read_unaligned`/`write_unaligned` instead.
|
||||
|
||||
## Rationale
|
||||
|
||||
- Packed structs have no padding, so fields may be misaligned
|
||||
- References must be aligned; misaligned references are UB
|
||||
- Even implicit references (method calls, match) can cause UB
|
||||
|
||||
## Bad Example
|
||||
|
||||
```rust
|
||||
#[repr(C, packed)]
|
||||
struct Packet {
|
||||
header: u8,
|
||||
value: u32, // Misaligned! At offset 1, not 4
|
||||
data: u64, // Misaligned! At offset 5, not 8
|
||||
}
|
||||
|
||||
fn bad_reference(p: &Packet) -> &u32 {
|
||||
&p.value // UB: Creates misaligned reference!
|
||||
}
|
||||
|
||||
fn bad_match(p: &Packet) {
|
||||
match p.value { // UB: Match creates a reference
|
||||
0 => {},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn bad_method(p: &Packet) {
|
||||
p.value.to_string(); // UB: Method call creates reference
|
||||
}
|
||||
|
||||
fn bad_borrow(p: &mut Packet) {
|
||||
let v = &mut p.value; // UB: Misaligned mutable reference
|
||||
*v = 42;
|
||||
}
|
||||
```
|
||||
|
||||
## Good Example
|
||||
|
||||
```rust
|
||||
#[repr(C, packed)]
|
||||
struct Packet {
|
||||
header: u8,
|
||||
value: u32,
|
||||
data: u64,
|
||||
}
|
||||
|
||||
// DO: Copy out the value
|
||||
fn good_read(p: &Packet) -> u32 {
|
||||
p.value // Copies the value, no reference created
|
||||
}
|
||||
|
||||
// DO: Use addr_of! for raw pointer (Rust 2021+)
|
||||
fn good_ptr_read(p: &Packet) -> u32 {
|
||||
// SAFETY: read_unaligned handles misalignment
|
||||
unsafe {
|
||||
std::ptr::addr_of!(p.value).read_unaligned()
|
||||
}
|
||||
}
|
||||
|
||||
// DO: Use addr_of_mut! for writing
|
||||
fn good_ptr_write(p: &mut Packet, value: u32) {
|
||||
// SAFETY: write_unaligned handles misalignment
|
||||
unsafe {
|
||||
std::ptr::addr_of_mut!(p.value).write_unaligned(value);
|
||||
}
|
||||
}
|
||||
|
||||
// DO: Create accessor methods
|
||||
impl Packet {
|
||||
fn value(&self) -> u32 {
|
||||
unsafe { std::ptr::addr_of!(self.value).read_unaligned() }
|
||||
}
|
||||
|
||||
fn set_value(&mut self, value: u32) {
|
||||
unsafe { std::ptr::addr_of_mut!(self.value).write_unaligned(value); }
|
||||
}
|
||||
|
||||
fn data(&self) -> u64 {
|
||||
unsafe { std::ptr::addr_of!(self.data).read_unaligned() }
|
||||
}
|
||||
}
|
||||
|
||||
// DO: Consider using byte arrays + from_ne_bytes
|
||||
#[repr(C, packed)]
|
||||
struct PacketBytes {
|
||||
header: u8,
|
||||
value: [u8; 4], // Store as bytes
|
||||
data: [u8; 8],
|
||||
}
|
||||
|
||||
impl PacketBytes {
|
||||
fn value(&self) -> u32 {
|
||||
u32::from_ne_bytes(self.value) // Safe, no alignment issue
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Safe Alternatives
|
||||
|
||||
```rust
|
||||
// Alternative 1: Don't use packed
|
||||
#[repr(C)]
|
||||
struct AlignedPacket {
|
||||
header: u8,
|
||||
_pad: [u8; 3],
|
||||
value: u32,
|
||||
data: u64,
|
||||
}
|
||||
|
||||
// Alternative 2: Use zerocopy crate
|
||||
// use zerocopy::{AsBytes, FromBytes};
|
||||
|
||||
// Alternative 3: Use bytemuck
|
||||
// use bytemuck::{Pod, Zeroable};
|
||||
```
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Am I creating references to packed struct fields?
|
||||
- [ ] Am I using addr_of! / addr_of_mut! for field access?
|
||||
- [ ] Am I using read_unaligned / write_unaligned?
|
||||
- [ ] Would a byte array representation be safer?
|
||||
|
||||
## Related Rules
|
||||
|
||||
- `ptr-04`: Don't dereference misaligned pointers
|
||||
- `mem-01`: Choose appropriate data layout
|
||||
Reference in New Issue
Block a user