rust-lang / rust-lang/rust-bindgen

Aligned padding wrapper shifts a following flexible-array member

Open
#3,406 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

rust-for-linux
Dominant language
Rust
Stars
5.3k
Forks
829
Avg merge
1d 1h
Merged PRs (30d)
15

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with the provided bindgen invocation and compare current main with 0.72.1, then read the padding changes from #3280 and the regression context in #3279. Trace the generated aligned wrapper and flexible-array layout; done means values remains at C offset 16 and the helper-based comparisons pass for the reported lengths and optimization levels.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, cpp, rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.