Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

Writing secure C++ software requires action at every layer: the compiler, the linker, the runtime library, and your own coding practices. MSVC provides a comprehensive set of hardening flags that detect vulnerabilities at compile time and enable operating system defenses at runtime. When combined with safe coding practices — using the secure CRT, smart pointers, and integer-safe arithmetic — these mitigations dramatically reduce your application’s attack surface.

Compiler Security Flags

/GS — Buffer Security Check

The /GS flag (enabled by default) inserts stack canary cookies into functions that have local buffers or use exception handling. When a buffer overrun corrupts the stack, the canary mismatch is detected before the function returns, and execution is terminated.
cl /GS myapp.cpp        # enabled by default
cl /GS- myapp.cpp       # explicitly disable (not recommended)
// /GS protects against overruns like this:
void vulnerable(const char* input) {
    char buf[64];
    strcpy(buf, input);  // if input > 63 chars, /GS detects the overrun
}
Never disable /GS in production code. The performance overhead is negligible (typically under 1%) while the security benefit is substantial.

/sdl — Security Development Lifecycle Checks

The /sdl flag elevates a subset of security-relevant warnings to errors and enables additional secure code generation features beyond /GS:
cl /sdl /W3 myapp.cpp
/sdl promotes these categories of issues to build errors:
  • Uninitialized variable usage that could leak sensitive data
  • Unsafe use of deprecated CRT functions (e.g., strcpy, sprintf)
  • Unsafe conversion patterns identified by the analyzer
Microsoft recommends enabling /sdl for all new projects. Combined with /W4 and /WX, it creates a strict, secure-by-default build environment that catches many vulnerabilities at compile time rather than at runtime.

Linker Security Flags

/SAFESEH — Safe Exception Handlers

The /SAFESEH linker flag produces a table of registered exception handler addresses in the image. The OS validates exception handler addresses at runtime against this table, preventing attackers from hijacking exception handling to redirect execution to injected shellcode.
link /SAFESEH myapp.obj
/SAFESEH applies only to x86 (32-bit) binaries. x64 binaries use a different exception model (table-based unwinding) that is inherently safe and does not require this flag.

/DYNAMICBASE — Address Space Layout Randomization (ASLR)

ASLR randomizes the load address of your executable and its DLLs each time the process starts, making it far harder for attackers to predict where code or data lives in memory.
link /DYNAMICBASE myapp.obj
For 64-bit binaries, pair with /HIGHENTROPYVA to use the full 64-bit address space for rebasing:
link /DYNAMICBASE /HIGHENTROPYVA myapp.obj

/NXCOMPAT — Data Execution Prevention (DEP)

DEP (NX/XD bit) prevents the CPU from executing code from pages that contain only data — the stack, the heap, and global data segments. This defeats classic shellcode injection attacks.
link /NXCOMPAT myapp.obj
64-bit binaries are DEP-compatible by default. For 32-bit binaries, this flag is required to opt in.

Control Flow Guard — /guard:cf

Control Flow Guard (CFG) inserts compiler-generated checks before every indirect function call (call rax, jmp [rsp+...], vtable dispatches). At runtime, the OS validates each call target against a bitmap of valid call sites, preventing attackers from redirecting control flow through corrupted function pointers.
cl /guard:cf myapp.cpp
link /guard:cf myapp.obj
// CFG protects indirect calls:
typedef void (*Callback)(int);

void Invoke(Callback fn, int value) {
    fn(value);  // CFG verifies fn is a legitimate call target at runtime
}
CFG is one of the most effective mitigations against modern exploitation techniques. Enable it for all shipping binaries. The runtime overhead is typically less than 1% for most workloads.

CET Shadow Stack — /CETCOMPAT

Intel’s Control-flow Enforcement Technology (CET) Shadow Stack maintains a second, hardware-protected call stack used only for return addresses. Return-oriented programming (ROP) attacks that corrupt the main stack’s return addresses are detected because the shadow stack values don’t match.
link /CETCOMPAT myapp.obj
cl /GS /sdl /W4 /WX /guard:cf /Zi /O2 myapp.cpp

Secure CRT Functions

The C Runtime Library (CRT) ships secure replacements for every unsafe string and buffer function. These functions take an additional destSize parameter and return an errno_t error code, ensuring that writes never exceed the destination buffer.
#include <string.h>   // unsafe versions
#include <stdlib.h>

// UNSAFE — no bounds checking, undefined behavior on overflow
char dest[10];
strcpy(dest, "this is way too long");   // buffer overflow!
sprintf(dest, "%s", veryLongString);    // buffer overflow!
gets(dest);                             // removed in C11 for this reason

// SAFE — secure CRT equivalents
char dest[10];
strcpy_s(dest, sizeof(dest), "hello");      // truncates safely, returns error
sprintf_s(dest, sizeof(dest), "%s", str);   // always null-terminates
// gets() has no safe equivalent — use fgets() or scanf_s()

Common Secure CRT Replacements

Unsafe FunctionSecure ReplacementNotes
strcpystrcpy_sRequires destination size
strcatstrcat_sRequires destination size
sprintfsprintf_sRequires destination size
getsfgets / scanf_sgets removed in C11
strtokstrtok_sThread-safe, context pointer
wcscpywcscpy_sWide-character equivalent
memcpy (overlapping)memmoveFor overlapping regions
scanfscanf_sRequires size for %s and %c
When you compile with /sdl, use of deprecated unsafe functions like strcpy and sprintf is promoted to a build error. This is intentional — migrate to the _s variants.

Smart Pointers and RAII

Raw new and delete are one of the most common sources of memory bugs: leaks, double-frees, and use-after-free. Prefer RAII-based smart pointers from <memory>:
#include <memory>
#include <vector>
#include <string>

// BAD: raw ownership — easy to double-free or leak
Widget* CreateWidget() {
    return new Widget();   // who calls delete?
}

// GOOD: unique_ptr — single owner, automatically freed
std::unique_ptr<Widget> CreateWidget() {
    return std::make_unique<Widget>();
}

// GOOD: shared_ptr — shared ownership with reference counting
std::shared_ptr<Session> OpenSession(const std::string& host) {
    return std::make_shared<Session>(host);
}

// GOOD: prefer value semantics and standard containers
std::vector<int> LoadData(size_t count) {
    std::vector<int> data(count);  // no manual allocation
    // ...
    return data;  // move semantics — no copy overhead
}
The C++ Core Guidelines rule R.11 (enabled by /analyze with the Core Check rule set) flags every use of explicit new and delete. Use it to systematically migrate legacy code to smart pointers.

Integer Safety with SafeInt

Integer overflows in size calculations are a common source of heap overflows. The SafeInt library wraps arithmetic operations and throws on overflow:
#include <safeint.h>
using namespace msl::utilities;

void AllocateBuffer(size_t elementCount, size_t elementSize) {
    // UNSAFE: elementCount * elementSize can overflow size_t
    // size_t totalBytes = elementCount * elementSize;

    // SAFE: SafeInt throws SafeIntException on overflow
    SafeInt<size_t> safeCount(elementCount);
    size_t totalBytes = safeCount * elementSize;  // exception on overflow

    auto buffer = std::make_unique<char[]>(totalBytes);
    // ...
}

User Accounts and Least Privilege

Run your application with the minimum required privileges. Developers who run as Administrator expose their machines — and their customers — to unnecessary risk:
  • Test your application running as a standard user to surface privilege-related issues early.
  • Use User Account Control (UAC) manifest settings to request elevation only when genuinely required.
  • Embed the appropriate requestedExecutionLevel in your application manifest:
<!-- app.manifest -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
  <security>
    <requestedPrivileges>
      <!-- Use "asInvoker" unless elevation is truly required -->
      <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
    </requestedPrivileges>
  </security>
</trustInfo>

Spectre Mitigations (/Qspectre)

Speculative execution side-channel attacks (Spectre/Meltdown) can leak sensitive data through CPU microarchitectural state. The /Qspectre flag inserts LFENCE barrier instructions at vulnerable speculative load sites:
cl /Qspectre myapp.cpp
Enable /Qspectre for code that processes sensitive data and operates across a trust boundary (e.g., a service that processes untrusted user input). Measure the performance impact on your specific workload before enabling globally, as it can be significant in tight loops. Use the __declspec(spectre(nomitigation)) attribute to selectively disable mitigations in performance-critical paths that have been reviewed as safe.

Security Checklist

  • /GS — enabled (default)
  • /sdl — SDL checks enabled
  • /guard:cf — Control Flow Guard enabled
  • /W4 /WX — high warning level, warnings as errors
  • /analyze — static analysis enabled
  • /DYNAMICBASE — ASLR enabled
  • /HIGHENTROPYVA — high-entropy ASLR (64-bit)
  • /NXCOMPAT — DEP enabled
  • /SAFESEH — safe exception handlers (x86)
  • /guard:cf — CFG linker flag also set
  • /CETCOMPAT — CET Shadow Stack compatible
  • Use strcpy_s, sprintf_s, strcat_s instead of unsafe equivalents
  • Use std::unique_ptr / std::shared_ptr instead of raw new/delete
  • Use std::vector and std::string instead of raw arrays
  • Use SafeInt for size arithmetic that feeds allocations
  • Run /analyze and address all Error-severity warnings
  • Enable C++ Core Guidelines checker rules
  • Run as standard user during development and testing

Build docs developers (and LLMs) love