rust-lang / rust-lang/rust-bindgen
Generated bitfield accessors shift by 64 when a field spans a 65-bit window
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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