Skip to content

AD3036: EnableControlFlowIntegrity

Summary

Property Value
ID AD3036
Name EnableControlFlowIntegrity
Category Security
Severity Warning
Applies to ELF (Linux/Unix)

Description

Control Flow Integrity (CFI) is a security mechanism that restricts the targets of indirect control flow transfers (function calls through pointers, virtual method calls, etc.) to valid destinations. This prevents many exploitation techniques that rely on hijacking program control flow.

Why This Matters

CFI provides the most comprehensive software-based protection against control-flow hijacking attacks. It validates every indirect branch, making exploitation extremely difficult even with memory corruption.

The Control-Flow Attack Problem

Attackers exploit indirect branches:

void (*func_ptr)(int);
func_ptr = legitimate_function;
// ... memory corruption ...
func_ptr = attacker_function;  // Hijacked!
(*func_ptr)(42);  // Calls attacker code

How CFI Protects

Without CFI:
  Indirect call → Any address → Attacker's code

With CFI:
  Indirect call → Must match allowed type signature
                → Only valid functions of that type allowed
                → Invalid target = abort

CFI Check Types

Check Protects Against
cfi-vcall Virtual function hijacking
cfi-nvcall Non-virtual member call hijacking
cfi-derived-cast Bad downcasts
cfi-unrelated-cast Invalid type casts
cfi-icall Function pointer hijacking

Why LTO Is Required

Without LTO:
  file1.c: void (*fp)(int);  // Call here
  file2.c: void target(int); // Target here
  Compiler can't see both → weak CFI

With LTO:
  Entire program visible
  All valid targets known
  Precise CFI enforcement

Attack Difficulty with CFI

Attack Technique Without CFI With CFI
ROP Works Harder (return-type checks)
Function pointer overwrite Works Blocked (type mismatch)
Vtable hijacking Works Blocked (virtual call check)
JOP Works Blocked (branch checks)

Performance Characteristics

Aspect Overhead
Indirect calls Small check
Virtual calls Small check
Direct calls None
Overall 1-5% typical

Cross-DSO CFI

For shared libraries, use -fsanitize-cfi-cross-dso:

Without cross-DSO:
  CFI only within binary
  Library calls unchecked

With cross-DSO:
  CFI across library boundaries
  Comprehensive protection

Clang's CFI implementation provides several types of protection: - cfi-vcall: Virtual function call validation - cfi-nvcall: Non-virtual member function call validation - cfi-derived-cast: Derived class cast validation - cfi-unrelated-cast: Unrelated class cast validation - cfi-icall: Indirect function call validation

CFI requires Link-Time Optimization (LTO) to work across translation units.

Category

Security

Resolution

Enable CFI when building with Clang:

# Enable all CFI checks (recommended)
clang -flto -fsanitize=cfi -fvisibility=hidden -o output source.c

# Enable specific CFI checks
clang -flto -fsanitize=cfi-vcall,cfi-icall -fvisibility=hidden -o output source.c

# For cross-DSO CFI (libraries)
clang -flto -fsanitize=cfi -fsanitize-cfi-cross-dso -fvisibility=hidden -o output source.c

For CMake projects:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto -fsanitize=cfi -fvisibility=hidden")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -fsanitize=cfi -fvisibility=hidden")

Detection

This rule detects CFI through multiple methods:

  1. Symbol-based detection: Checks for CFI runtime symbols:
  2. __cfi_check
  3. __cfi_slowpath
  4. __cfi_slowpath_diag
  5. __ubsan_handle_cfi_check_fail

  6. DWARF-based detection: Checks for -fsanitize=cfi in compiler flags recorded in debug information

Applicability

This rule applies to: - ELF executables (ET_EXEC) - ELF shared libraries (ET_DYN that are not PIE executables) - Binaries compiled with Clang/LLVM

Examples

Pass

Binary compiled with: clang -flto -fsanitize=cfi -fvisibility=hidden

Fail

Binary compiled without CFI instrumentation

Notes

  • CFI requires LTO (-flto) to function properly
  • The -fvisibility=hidden flag is recommended to reduce the attack surface
  • Cross-DSO CFI (-fsanitize-cfi-cross-dso) enables CFI checks across shared library boundaries
  • CFI may have a small runtime performance overhead

References