Case 63: Bitfield Width Changed¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux |
| Flags | ABI break, API break |
Detected ChangeKinds |
field_bitfield_changed |
| Source files | examples/case63_bitfield_changed/ |
Category: Type Layout | Verdict: ๐ด BREAKING
Verdict and consumer impact¶
The mode bitfield in RegMap widens from 3 bits to 5 bits. sizeof(RegMap)
stays 4 bytes โ there's no alignment padding between bitfields sharing a
storage unit โ but every subsequent bitfield (channel, priority,
reserved) shifts to different bit positions. A consumer compiled against
v1 reads priority from the old bit range and gets a value from the middle
of an unrelated field: no crash, no diagnostic, just a silently wrong number.
Recompilation against v2 is mandatory.
Old/new diff¶
| Field | v1 (bits) | v2 (bits) | Change |
|---|---|---|---|
enable |
0 | 0 | unchanged |
mode |
1-3 (3 bits) | 1-5 (5 bits) | widened +2 bits |
channel |
4-7 | 6-9 | shifted +2 |
priority |
8-15 | 10-17 | shifted +2 |
reserved |
16-31 (16 bits) | 18-31 (14 bits) | shrunk -2 bits |
abicheck command¶
gcc -shared -fPIC -g v1.c -o libfoo_v1.so
gcc -shared -fPIC -g v2.c -o libfoo_v2.so
abicheck compare libfoo_v1.so libfoo_v2.so
Expected abicheck finding¶
Verdict: BREAKING (exit 4)
- type_field_offset_changed: Field offset changed: RegMap::channel (4 -> 6 bits)
> Old code reads/writes fields at stale offsets; silent data corruption.
- type_field_offset_changed: Field offset changed: RegMap::priority (8 -> 10 bits)
- type_field_offset_changed: Field offset changed: RegMap::reserved (16 -> 18 bits)
- field_bitfield_changed: Bitfield layout changed: RegMap::mode (bits=3 -> bits=5)
> Bit-field width or offset changed; old code reads/writes wrong bits.
- field_bitfield_changed: Bitfield layout changed: RegMap::reserved (bits=16 -> bits=14)
Minimum evidence¶
min_evidence: L1 โ DWARF's DW_AT_bit_size/DW_AT_bit_offset (or
DW_AT_data_bit_offset) attributes on each bitfield member record the exact
bit layout for both versions; no public headers needed.
Why abicheck catches it¶
abicheck compares each struct member's bit width and bit offset from DWARF
directly. Because sizeof(RegMap) doesn't change and the member names and
declared types (uint32_t) are identical, only the bit-level DWARF
attributes distinguish v1 from v2 โ a check that requires debug info, since
plain symbol-table or type-name comparison would report no change at all.
Runtime failure demonstration¶
Severity: CRITICAL
Scenario: compile app against v1, swap in v2 .so without recompile.
# Build old library + app
gcc -shared -fPIC -g v1.c -o libfoo.so
gcc -g app.c -L. -lfoo -Wl,-rpath,. -o app
./app
# โ mode = 2 (expected 2)
# โ channel = 5 (expected 5)
# โ priority = 128 (expected 128)
# โ raw word = 0x00008055
# Swap in new library (no recompile)
gcc -shared -fPIC -g v2.c -o libfoo.so
./app
# โ mode = 2 (expected 2)
# โ channel = 4 (expected 5)
# โ priority = 1 (expected 128)
# โ raw word = 0x00020145
# โ CORRUPTION: bitfield layout mismatch โ app reads v1 bit positions but
# library wrote v2 bit positions!
Why CRITICAL: v2's regmap_init() writes priority=128 into bits
10-17, but the app (compiled against v1) reads bits 8-15 โ extracting a
completely different value. No crash occurs; the data is silently wrong. In
a hardware register context, this could program the wrong DMA priority,
causing system instability.
Safe redesign¶
Never change bitfield widths in a public struct. If more bits are needed,
consume them from an existing reserved field instead of widening a field
that shifts everything after it, ship a new struct version
(RegMapV2), or hide the register layout entirely behind accessor
functions.
Real-world example: the Linux kernel's iphdr (IP header) structure
uses bitfields for version and IHL โ any width change there would corrupt
every packet parsed by userspace tools like tcpdump that embed the struct
layout at compile time. The Windows BITMAPINFOHEADER uses bitfields for
color masks; driver compatibility across Windows versions depends on those
widths staying frozen.
Cross-tool comparison¶
References¶
Source files¶
CMakeLists.txtapp.cv1.cv1.hv2.cv2.h
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.