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:
- Memory corruption: Attacker exploits a bug to overwrite a function pointer or vtable entry
- Indirect call hijacking: When the application makes an indirect call through the corrupted pointer, execution redirects to attacker-controlled code
- 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:
Linker flag:
Project Properties¶
- Open Project Properties
- Navigate to C/C++ → Code Generation
- Set "Control Flow Guard" to "Yes (/guard:cf)"
- Navigate to Linker → Advanced
- Ensure "Guard" includes "CF"
CMake¶
MSBuild¶
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+) |