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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    rust-for-linuxIssues relevant to the Rust for Linux project

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions