Case 41: Type-Level Changes¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux, macOS |
| Flags | ABI break, API break |
Detected ChangeKinds |
โ |
| Source files | browse on GitHub |
Category: Type / Enum Changes | Verdict: BREAKING
What changes¶
| Version | Definition |
|---|---|
| v1 | struct LegacyConfig exists; AlignedBuffer aligned to 8; priority_t has PRIO_MAX=3 |
| v2 | LegacyConfig removed, NewConfig added; AlignedBuffer aligned to 64; PRIO_URGENT inserted, PRIO_MAX=4 |
Why this is a binary ABI break¶
struct LegacyConfigremoved โprocess_config()is also removed. Old binaries that callprocess_config()get an undefined symbol error at load time.struct NewConfigadded โ compatible by itself (no old code references it), but does not replaceLegacyConfigfor existing binaries.AlignedBufferalignment changed (8 to 64) โ old binaries allocateAlignedBufferon the stack with 8-byte alignment. The v2 library may assume 64-byte alignment (e.g., for SIMD or cache-line operations), causing misaligned access, crashes, or silent data corruption.PRIO_MAXsentinel changed (3 to 4) โ old binaries usingPRIO_MAXas an array bound or loop limit will be off by one. Code checkingif (p < PRIO_MAX)will have the old value3baked in, missing the newPRIO_URGENT=3level entirely.
Code diff¶
-struct LegacyConfig {
- int mode;
- int flags;
-};
+/* LegacyConfig REMOVED */
+
+struct NewConfig {
+ int mode;
+ int flags;
+ int version;
+};
-struct __attribute__((aligned(8))) AlignedBuffer {
+struct __attribute__((aligned(64))) AlignedBuffer {
char data[64];
};
typedef enum {
PRIO_LOW = 0,
PRIO_MEDIUM = 1,
PRIO_HIGH = 2,
- PRIO_MAX = 3
+ PRIO_URGENT = 3,
+ PRIO_MAX = 4
} priority_t;
-void process_config(struct LegacyConfig *cfg);
+/* process_config removed */
Real Failure Demo¶
Severity: CRITICAL
Scenario: compile app against v1, swap in v2 .so without recompile.
# Build v1 lib + app
gcc -shared -fPIC -g v1.c -o libfoo.so
gcc -g app.c -I. -L. -lfoo -Wl,-rpath,. -o app
./app
# โ process_config(mode=1, flags=255)
# โ fill_buffer (alignof=8, sizeof=64)
# โ set_priority(PRIO_HIGH=2)
# โ PRIO_MAX = 3 (sentinel)
# Swap to v2 (no recompile of app)
gcc -shared -fPIC -g v2.c -o libfoo.so
./app
# โ ./app: symbol lookup error: ./app: undefined symbol: process_config
#
# If process_config reference were removed:
# - AlignedBuffer allocated with 8-byte alignment but library expects 64
# - PRIO_MAX is still 3 in the app but 4 in the library
Why CRITICAL: Type removal causes immediate link failure. Alignment mismatches can cause SIGSEGV on architectures that enforce alignment. Enum sentinel shifts cause off-by-one errors in bounds checks and array sizing โ a subtle, hard-to-debug corruption.
Reproduce manually¶
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -shared -fPIC -g v2.c -o libv2.so
abidw --out-file v1.xml libv1.so
abidw --out-file v2.xml libv2.so
abidiff v1.xml v2.xml
echo "exit: $?" # โ 12 (ABI change + breaking)
How to fix¶
Never remove public types or functions without a SONAME bump. Use opaque types so that
alignment changes are invisible to callers. Avoid using enum sentinel values as array
bounds in public APIs โ use a separate #define or function instead.
References¶
Source files¶
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.