rust-lang / rust-lang/rust-bindgen

Generated bitfield accessors shift by 64 when a field spans a 65-bit window

Open
#3,416 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Input C/C++ header
struct __attribute__((packed)) S {
    unsigned long long p : 2;
    unsigned long long x : 63;
};
Bindgen invocation
$ bindgen input.h \
    --rust-target 1.75 \
    --rust-edition 2021 \
    -- -std=gnu11 > bindings.rs
Actual output

The generated record layout is correct:

#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
pub struct S {
    pub _bitfield_1: __BindgenBitfieldUnit<[u8; 9usize]>,
}

impl S {
    #[inline]
    pub fn x(&self) -> ::std::os::raw::c_ulonglong {
        self._bitfield_1.get_const::<2usize, 63u8>() as u64 as _
    }
}

The generated helper calculates a nine-byte window and accumulates it in a
u64:

let bit_shift = BIT_OFFSET % 8;
let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8;
let mut val = 0u64;
let mut i = 0;
while i < bytes_needed {
    val |= (self.storage[start_byte + i].reverse_bits() as u64) << (i * 8);
    i += 1;
}

I ran the unchanged binding against the all-ones representation. Clang reports
that x occupies bits 2 through 64 and that S is nine bytes:

0:0-1 | unsigned long long p
0:2-64 | unsigned long long x
       | [sizeof=9, align=1]

The C accessor returns:

x=0x7fffffffffffffff

With overflow checks enabled, the generated Rust getter panics:

attempt to shift left with overflow

With an optimized build and overflow checks disabled, it returns:

x=0x3fffffffffffffff
What is wrong

x is 63 bits wide but begins at intra-byte offset 2. Its extraction window is
therefore 65 bits and spans nine bytes. When the loop reaches the ninth byte,
it shifts a u64 by 64. The setter paths perform the corresponding right shift
by 64 while extracting the ninth value and mask bytes.

The storage type and record layout are correct. The error is in the generated
getter, setter, raw accessor, and constructor method bodies.

Expected output

The generated accessors should handle a field whose width plus intra-byte
offset exceeds 64 bits without shifting a u64 by 64. Both builds should
produce:

x=0x7fffffffffffffff

The setters, raw setters, and new_bitfield_1 should also write a value that
the C declaration reads back as 0x7fffffffffffffff.

Impact

With current main, the generated getters, setters, raw accessors, and
constructor panic when overflow checks are enabled. In an optimized build, the
getter loses the highest bit, and the write paths store
0x3fffffffffffffff where C expects 0x7fffffffffffffff.

Bindgen 0.72.1 does not reproduce this regression.

Environment
bindgen: 0.72.0, current main 25b23474496e78f6a1fbf1c02cb66f11e4d176d0
clang/libclang: 15.0.7
rustc: 1.75.0
target: x86_64-unknown-linux-gnu
OS: Ubuntu 22.04.5 LTS, x86_64

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 with the generated __BindgenBitfieldUnit get_const helper and inspect the getter, setter, raw accessor, and new_bitfield_1 paths described in the issue. Reproduce using input.h and the shown bindgen invocation, then verify both checked and optimized builds preserve the high bit and that the C accessor reads 0x7fffffffffffffff.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, rust
Domain
devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.