Skip to content

Aligned padding wrapper shifts a following flexible-array member #3406

Description

@lwz23

Input C/C++ Header

struct phase8_align16_record {
    unsigned long long left;
    unsigned long long right;
} __attribute__((aligned(16)));

struct phase8_packet {
    unsigned char prefix;
    struct phase8_align16_record values[];
};

Bindgen Invocation

$ bindgen input.h \
    --output bindings.rs \
    --allowlist-type '^phase8_.*' \
    --no-layout-tests \
    --no-doc-comments \
    --formatter none \
    --rust-target 1.75 \
    -- -x c -std=gnu11

Actual Results

Current main generates this layout, shortened to the relevant fields:

#[repr(C, align(8))]
pub struct __BindgenOpaqueArray8<T>(pub T);

#[repr(C)]
#[repr(align(16))]
pub struct phase8_packet {
    pub prefix: u8,
    pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 15usize]>,
    pub values: __IncompleteArrayField<phase8_align16_record>,
}

The C ABI puts values at offset 16. The generated Rust type puts it at
offset 32:

C sizeof(packet without FAM): 16
C offsetof(values):           16
Rust size_of::<packet>():     32
Rust offset of values:        32

The padding calculation asks for 15 bytes with alignment 8. The generated
wrapper is an align-8 Rust type containing [u8; 15], so its actual size is
rounded up to 16. It also starts at offset 8 after Rust inserts alignment
before the wrapper. The next align-16 field therefore starts at 32.

This affects the generated helpers. With a C allocation and one array element:

C writes, Rust as_slice reads:
  C offset 16, Rust access 32, digest mismatch

Rust as_mut_slice writes, C reads:
  C offset 16, Rust access 32, digest mismatch
  back canary overwritten

Expected Results

The generated representation must keep values at offset 16. A plain
[u8; 15] padding field works for this case. Splitting implicit and explicit
padding would also be fine, as long as the actual Rust field placement agrees
with the C ABI.

Environment

bindgen current main: 9d26c6eddeff9192ddedb563192abe3128fc5aae
bindgen release:      0.72.1
clang:                15.0.7
rustc:                1.75.0
target:               x86_64-unknown-linux-gnu

This is a current-main regression. Bindgen 0.72.1 puts values at offset 16,
and both runtime directions match the C reference. I tested lengths 1, 2, and
7 at O0 and O2. Current main failed every helper-based comparison; 0.72.1 and
the raw-offset controls passed.

Additional notes

This appears to have started with #3280, which fixed #3279 by replacing
primitive opaque padding fields with explicitly aligned wrappers. #3279 was
about insufficient alignment on x86. This case is a different failure: a
padding byte count that is not a multiple of the wrapper alignment increases
the wrapper's physical size and shifts a following flexible-array member.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions