Skip to content

AD2031: EnableControlStackChecking

Summary

Property Value
ID AD2031
Name EnableControlStackChecking
Category Security
Severity Warning
Applies to PE (Windows) - Native binaries

Description

The /Gs compiler option controls the threshold for stack probes. When a function's local variables exceed the threshold, the compiler inserts calls to __chkstk to probe stack pages. This helps prevent stack overflow vulnerabilities by ensuring stack pages are committed before use.

How It Works

The rule examines the PDB file for /Gs compiler flags and checks for the presence of stack probing symbols. It also verifies that the security cookie (/GS) is enabled, which typically implies proper /Gs configuration.

Default Behavior

  • Default threshold is 4KB (one page)
  • /Gs0 probes every stack allocation
  • /Gs with no value uses the default threshold
  • Large thresholds (e.g., /Gs65536) may skip important probes

Why This Matters

Stack probing is a critical defense against stack clash attacks, where attackers exploit large stack allocations to bypass guard pages and corrupt adjacent memory regions. Without proper probing, a single function call could compromise memory protection.

The Stack Clash Attack

Stack clash exploits how the OS manages stack memory:

Normal stack growth:
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   Used Stack    β”‚  ← Stack pointer
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚   Guard Page    β”‚  ← Access = crash (protection)
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚  Unmapped/Heap  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Stack clash attack:
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   Used Stack    β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚   Guard Page    β”‚  ← SKIPPED!
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚  Heap/Other     β”‚  ← Corrupted!
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How the Attack Works

  1. Large allocation: Function allocates huge stack frame
  2. Skip guard page: Allocation jumps over guard page
  3. Write to stack: Actually writes to heap/other memory
  4. Corruption: Critical data structures modified
  5. Exploitation: Code execution or privilege escalation

Stack Probing Prevention

With proper /Gs, every page is touched:

// Large local array
char buffer[65536];  // 16 pages

// Without probing: Single SP adjustment
sub rsp, 65536  // May skip guard pages!

// With probing: Touch each page
call __chkstk   // Probes page by page
                // Hits guard page if stack exhausted

Threshold Configuration

Setting Behavior Use Case
/Gs (default) Probe for >4KB Normal code
/Gs0 Probe all allocations Security-critical
/Gs65536 Probe for >64KB DANGEROUS!
/Gs1 Nearly all probed Maximum protection

Real-World Impact

Stack clash vulnerabilities have affected:

System CVE Impact
Linux/glibc CVE-2017-1000364 Local privilege escalation
Linux kernel CVE-2017-1000365 Container escape
Windows Various Process-to-kernel attacks
BSD Multiple Privilege escalation

Performance Considerations

Stack probing overhead is minimal:

Scenario Overhead
Small functions (<4KB stack) Zero (no probe needed)
Large stack allocations Few cycles per page
Recursive algorithms May accumulate
Overall application <0.1% typically

The probe is a simple memory touch per pageβ€”extremely fast.

Why Guard Pages Aren't Enough

Guard Page Limitation Problem
Single page only Large jumps skip it
Between stack/heap Can be bypassed
No proactive checking Relies on access patterns

Stack probing ensures guard pages are actually encountered.

Stack probing prevents several attack vectors:

  • Stack clash attacks: Prevent stack from colliding with heap or other memory regions
  • Stack overflow exploits: Detect when stack grows beyond allocated space
  • Guard page bypasses: Ensure guard pages are touched before overflow

Without proper stack probing, an attacker could: 1. Allocate a large stack frame that skips the guard page 2. Corrupt adjacent memory regions 3. Achieve arbitrary code execution

Resolution

Use Default Stack Checking

cl.exe /Gs ...

Aggressive Stack Checking

For security-critical code, probe all allocations:

cl.exe /Gs0 ...

Project Properties

  1. Open Project Properties
  2. Navigate to C/C++ β†’ Code Generation
  3. Ensure "Security Check" is enabled (/GS)
  4. Set "Stack Probe Size" appropriately

CMake

if(MSVC)
    add_compile_options(/Gs /GS)
endif()

When to Suppress

This rule can be suppressed in the following scenarios:

  • Known stack usage: Functions with well-understood, bounded stack use
  • Performance-critical hot paths: Where probe overhead is unacceptable
  • Leaf functions: Small functions with minimal stack usage

Caveats

  • Large /Gs thresholds may not catch all stack issues
  • Stack probing adds some runtime overhead
  • Works in conjunction with /GS (buffer security check)

References