Be Careful with UB When Referencing #[repr(packed)] Struct Fields

2026-08-30 浏览 (1)


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

#[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

#[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

// 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

你可能感兴趣的文章

Do Not Expose Raw Pointers in Public APIs

Unsafe Code Authors Must Verify Safety Invariants

Ensure Consistent Data Layout for Custom Types

Add Safety Section in Docs for Public Unsafe Functions

Do Not Use Union Variants Across Different Lifetimes

Use MaybeUninit for Uninitialized Memory

Ensure C-ABI Compatibility for Strings Between Rust and C

Do Not Dereference Pointers Cast to Misaligned Types

Use Dedicated Opaque Type Pointers Instead of c_void for C Opaque Types

Choose Appropriate Data Layout for Struct/Tuple/Enum

  • 所属分类: AI
  • 本文标签: rust
  • 版权声明: 本文链接 https://seaxiang.com/blog/I1XshlcT