Skip to content

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


Source files

  • CMakeLists.txt
  • app.c
  • v1.c
  • v2.c

See also: Examples overview ยท All COMPATIBLE_WITH_RISK cases ยท Category: Risk.