Case 179: CET Protection Weakened¶
| Field | Value |
|---|---|
| Verdict | ๐ก COMPATIBLE_WITH_RISK |
| Category | Risk |
| Platforms | Linux |
| Flags | Bad practice |
Detected ChangeKinds |
cet_protection_weakened |
| Source files | examples/case179_cet_protection_weakened/ |
Category: Security Hardening (ELF/Loader) | Verdict: โ ๏ธ COMPATIBLE_WITH_RISK
Verdict and consumer impact¶
The source is unchanged; only the build flag differs (-fcf-protection=full
for v1, -fcf-protection=none for v2). dispatch()'s indirect call through
table[which & 1] is exactly what Intel CET's Indirect Branch Tracking
(IBT) defends: if an attacker corrupts that function pointer, a CET-enabled
binary refuses to jump anywhere that isn't a declared ENDBR64 landing
pad. v2 still works identically for legitimate input โ this is not a
functional regression, and abicheck correctly does not call it BREAKING โ
but the security posture regressed silently: nothing about the exported
API, symbol table, or types changed at all.
Old/new diff¶
Source is identical between v1.c and v2.c; only the compile flags differ:
| v1 | v2 |
|---|---|
gcc -shared -fPIC -fcf-protection=full v1.c |
gcc -shared -fPIC -fcf-protection=none v2.c |
abicheck command¶
gcc -shared -fPIC -g -fcf-protection=full v1.c -o libfoo_v1.so
gcc -shared -fPIC -g -fcf-protection=none v2.c -o libfoo_v2.so
abicheck compare libfoo_v1.so libfoo_v2.so
Expected abicheck finding¶
Verdict: COMPATIBLE_WITH_RISK (exit 0)
Deployment Risk Changes:
- cet_protection_weakened: CET protection weakened: IBT, SHSTK -> (none)
Minimum evidence¶
min_evidence: L0 โ read directly from the ELF .note.gnu.property
section's x86 feature bits; no DWARF, no headers, no build metadata needed.
Why abicheck catches it¶
abicheck reads each binary's .note.gnu.property note and compares the x86
ISA feature bits (GNU_PROPERTY_X86_FEATURE_1_IBT,
GNU_PROPERTY_X86_FEATURE_1_SHSTK) directly. v1's note advertises both
IBT and SHSTK; v2 has no .note.gnu.property section at all, so the
detector reports the drop from IBT, SHSTK to (none).
Runtime failure demonstration¶
There is deliberately no crash to demonstrate: dispatch() behaves
identically under both builds for legitimate callers โ the risk this case
encodes is exploitability under attack, not correctness under normal use,
so there's no observable effect on existing binaries.
gcc app.c v1.c -o app
./app
# โ dispatch(0, 5, 3) = 8 (add, expected 8)
# โ dispatch(1, 5, 3) = 2 (sub, expected 2)
The kind of change a functional test suite will never catch, and that only reading the binary's own hardening metadata reveals.
Safe redesign¶
Build security-sensitive libraries with -fcf-protection=full (or the
distro toolchain default that already enables it) and treat a regression
as a release blocker, not a style choice. If a specific translation unit
genuinely cannot support CET (rare โ usually hand-written assembly without
ENDBR64 landing pads), scope -fcf-protection=none to that file only,
not the whole library.
Cross-tool comparison¶
readelf -n libfoo_v1.so | grep -A2 propert # x86 feature: IBT, SHSTK
readelf -n libfoo_v2.so | grep -A2 propert # (no .note.gnu.property section)
readelf confirms the raw property-note difference abicheck's
cet_protection_weakened detector reads; abidiff/ABICC do not model ELF
hardening notes at all, so this class of finding is abicheck-specific.
References¶
- Intel CET: Control-flow Enforcement Technology
- GCC:
-fcf-protection - Related cases:
case135_stack_canary_removed,case134_relro_weakened
Source files¶
CMakeLists.txtapp.cv1.cv2.c
See also: Examples overview ยท All COMPATIBLE_WITH_RISK cases ยท Category: Risk.