Files
Sprimo/skills/unsafe-checker/rules/ffi-13-data-layout.md
2026-02-12 22:58:33 +08:00

3.1 KiB

id, original_id, level, impact
id original_id level impact
ffi-13 P.UNS.FFI.13 P HIGH

Ensure Consistent Data Layout for Custom Types

Summary

Types shared between Rust and C must have #[repr(C)] to ensure the memory layout matches what C expects.

Rationale

  • Rust's default layout is unspecified and may change
  • C has specific, standardized layout rules
  • Mismatched layouts cause memory corruption

Bad Example

// DON'T: Rust layout for FFI types
struct BadStruct {
    a: u8,
    b: u32,
    c: u8,
}
// Rust may reorder to: b, a, c (for better packing)
// C expects: a, padding, b, c, padding

extern "C" {
    fn use_struct(s: *const BadStruct);  // Layout mismatch!
}

// DON'T: Assume Rust enum layout matches C
enum BadEnum {
    A,
    B(i32),
    C { x: u8, y: u8 },
}
// Rust enum layout is complex and not C-compatible

Good Example

// DO: Use repr(C) for FFI structs
#[repr(C)]
struct GoodStruct {
    a: u8,      // offset 0
    // 3 bytes padding
    b: u32,     // offset 4
    c: u8,      // offset 8
    // 3 bytes padding
}
// Total size: 12, align: 4

// DO: Use repr(C) for enums with explicit discriminant
#[repr(C)]
enum GoodEnum {
    A = 0,
    B = 1,
    C = 2,
}
// Equivalent to C: enum { A = 0, B = 1, C = 2 };

// DO: For complex enums, use tagged unions
#[repr(C)]
struct TaggedUnion {
    tag: GoodEnum,
    data: GoodUnionData,
}

#[repr(C)]
union GoodUnionData {
    a: (),         // For GoodEnum::A
    b: i32,        // For GoodEnum::B
    c: [u8; 2],    // For GoodEnum::C
}

// DO: Verify layout at compile time
const _: () = {
    assert!(std::mem::size_of::<GoodStruct>() == 12);
    assert!(std::mem::align_of::<GoodStruct>() == 4);
};

Layout Verification

use std::mem::{size_of, align_of, offset_of};

#[repr(C)]
struct Verified {
    a: u8,
    b: u32,
    c: u8,
}

// Compile-time layout verification
const _: () = {
    assert!(size_of::<Verified>() == 12);
    assert!(align_of::<Verified>() == 4);
    // offset_of! requires nightly or crate
    // assert!(offset_of!(Verified, a) == 0);
    // assert!(offset_of!(Verified, b) == 4);
    // assert!(offset_of!(Verified, c) == 8);
};

// Runtime verification
#[test]
fn verify_layout() {
    assert_eq!(size_of::<Verified>(), 12);
    assert_eq!(align_of::<Verified>(), 4);

    let v = Verified { a: 0, b: 0, c: 0 };
    let base = &v as *const _ as usize;

    assert_eq!(&v.a as *const _ as usize - base, 0);
    assert_eq!(&v.b as *const _ as usize - base, 4);
    assert_eq!(&v.c as *const _ as usize - base, 8);
}

repr Options

Attribute Effect
#[repr(C)] C-compatible layout
#[repr(C, packed)] C layout, no padding
#[repr(C, align(N))] C layout, minimum align N
#[repr(transparent)] Same layout as single field
#[repr(u8)] etc. Enum discriminant type

Checklist

  • Is every FFI struct marked #[repr(C)]?
  • Is every FFI enum using explicit discriminants?
  • Have I verified the layout matches the C header?
  • Have I added compile-time assertions?
  • mem-01: Choose appropriate data layout
  • ffi-14: Types in FFI should have stable layout