Skip to content

AD2008: EnableControlFlowGuard

Summary

Property Value
ID AD2008
Name EnableControlFlowGuard
Category Security
Severity Error
Applies to PE (Windows)

Description

Binaries should enable the compiler Control Flow Guard (CFG) feature at build time to prevent attackers from redirecting execution to unexpected, unsafe locations.

CFG analyzes and discovers all indirect-call instructions at compilation and link time. It also injects a check that precedes every indirect call in code that ensures the target is an expected, safe location. If that check fails at runtime, the operating system will close the program.

How It Works

The rule checks for the IMAGE_GUARD_CF_INSTRUMENTED flag in the PE load configuration directory. This flag indicates that CFG instrumentation is present in the binary.

CFG maintains a bitmap of valid call targets. Before each indirect call, the generated code checks if the target address is in the valid set. If not, the process is terminated.

Why This Matters

Control Flow Guard (CFG) is Microsoft's implementation of Control Flow Integrity (CFI), protecting against one of the most powerful exploit techniques: control flow hijacking through indirect calls.

The Attack CFG Prevents

Modern exploits often follow this pattern:

  1. Memory corruption: Attacker exploits a bug to overwrite a function pointer or vtable entry
  2. Indirect call hijacking: When the application makes an indirect call through the corrupted pointer, execution redirects to attacker-controlled code
  3. Code reuse: Attacker chains together existing code fragments (ROP/JOP gadgets)

CFG breaks step 2 by validating that each indirect call target is a legitimate function entry point.

How CFG Works

Without CFG:
    call [rax]  ; Call whatever address is in rax

With CFG:
    call __guard_check_icall  ; Validate rax
    call [rax]                ; Only reached if valid

The validation checks against a bitmap of valid call targets generated at compile time. Invalid targets cause immediate process termination.

CFG Coverage Statistics

Microsoft's research shows CFG significantly reduces exploitability:

Metric Result
Indirect calls protected >90% in typical binaries
Valid targets reduced Often 100x fewer than all functions
Exploit success rate Dramatically reduced

Performance Overhead

CFG is designed for production use with minimal impact:

Metric Overhead
CPU 1-2% typical
Memory Small bitmap (1 bit per 8 bytes of code)
Startup Negligible

The performance cost is well worth the security benefit for virtually all applications.

Hardware Acceleration

On modern processors (Intel Tiger Lake, AMD Zen 3, and newer), CFG can use hardware-accelerated validation, reducing overhead to near-zero.

Defense in Depth

CFG works best in combination with other mitigations:

Mitigation Protects Against
ASLR Predicting code locations
DEP/NX Executing injected shellcode
CFG Hijacking indirect calls
CET Shadow Stack Corrupting return addresses

Known Limitations

CFG is not perfect:

  • Return addresses: CFG doesn't protect returns (use CET Shadow Stack for that)
  • Valid-to-valid jumps: Attacker can still chain valid function calls
  • Partial coverage: JIT code and some edge cases may not be protected

Despite these limitations, CFG significantly raises the bar for exploitation.

Resolution

Enable CFG in Visual Studio

Compiler flag:

cl.exe /guard:cf ...

Linker flag:

link.exe /guard:cf ...

Project Properties

  1. Open Project Properties
  2. Navigate to C/C++ → Code Generation
  3. Set "Control Flow Guard" to "Yes (/guard:cf)"
  4. Navigate to Linker → Advanced
  5. Ensure "Guard" includes "CF"

CMake

if(MSVC)
    add_compile_options(/guard:cf)
    add_link_options(/guard:cf)
endif()

MSBuild

<PropertyGroup>
  <ControlFlowGuard>Guard</ControlFlowGuard>
</PropertyGroup>

When to Suppress

This rule can be suppressed in the following scenarios:

  • Older Windows targets: CFG requires Windows 8.1 Update 3 or later
  • Performance-critical code: Rare cases where 1-2% overhead is unacceptable
  • Custom loaders: Non-standard loading scenarios that break CFG
  • Kernel drivers: May require special CFG configuration

Caveats

  • Requires Windows 8.1 Update 3 or Windows 10+
  • All linked libraries should also be compiled with CFG
  • Some JIT compilers need special handling for CFG
  • Mixing CFG and non-CFG code in the same module reduces protection

Additional CFG Options

Option Description
/guard:cf Enable CFG
/guard:cf,longjmp Also protect setjmp/longjmp
/guard:cf,nochecks CFG metadata only (for DLLs that don't make indirect calls)
/guard:ehcont Enable EH continuation metadata (Windows 10 20H1+)

References