Is it UB to byte-copy memory containing repr(C) structs with padding?

⚓ Rust    📅 2026-01-18    👤 surdeus    👁️ 7      

surdeus

Hi,

I have a large contiguous block of memory (in reality mmap’d shared memory; here simplified as a u8 array). Into this memory I write several different #[repr(C)] structs sequentially using std::ptr::write::<T>(). Some of these structs may contain implicit padding.

Later, I want to copy the used portion of the memory as raw bytes (just a byte-for-byte copy).

My concern is the following:

  • #[repr(C)] structs may contain implicit padding.
  • Padding bytes are allowed to be uninitialized.
  • std::ptr::write::<T>() will cause the u8 array to contain uninitialized bytes as well, despite being initialized before.
  • Copying the memory as u8 would read those padding bytes.
  • Reading uninitialized memory is UB.

Is this reasoning correct? If so, what is the correct way to handle this pattern?

Example

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

#[repr(C)]
struct A {
    x: u8,
    y: u32,
}

#[repr(C)]
struct B {
    a: u32,
    b: u64,
}

fn align_up(off: usize, align: usize) -> usize {
    (off + align - 1) & !(align - 1)
}

fn main() {
    let mut region = [0u8; 1024];
    let mut snapshot = [0u8; 1024];
    let mut cursor = 0usize;

    unsafe {
        cursor = align_up(cursor, align_of::<A>());
        ptr::write(region.as_mut_ptr().add(cursor) as *mut A, A { x: 1, y: 2 });
        cursor += size_of::<A>();

        cursor = align_up(cursor, align_of::<B>());
        ptr::write(region.as_mut_ptr().add(cursor) as *mut B, B { a: 3, b: 4 });
        cursor += size_of::<B>();

        // Later: byte-copy the populated prefix
        ptr::copy_nonoverlapping(region.as_ptr(), snapshot.as_mut_ptr(), cursor);
    }
}

3 posts - 2 participants

Read full topic

🏷️ Rust_feed