Security of C++ Applications: Top 10 Vulnerabilities and Protection Methods
C++ is a powerful systems programming language, but its flexibility and direct memory access make applications vulnerable to serious attacks. According to OWASP and CWE, many critical vulnerabilities in industrial software (from games to avionics) arise precisely from errors in C++ code. In this article, we will analyze the top 10 most dangerous vulnerabilities, provide code examples, and show how to avoid them.
1. Buffer Overflow
A classic vulnerability that allows an attacker to overwrite adjacent memory areas. It occurs when copying data without checking the size.
// Vulnerable code#include <cstring>void vulnerable(char* input) { char buffer[10]; strcpy(buffer, input); // Danger: if input is longer than 9 characters}// Secure versionvoid secure(char* input, size_t len) { char buffer[10]; if (len < sizeof(buffer)) { strncpy(buffer, input, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\\0'; }}Protection: use std::string, std::vector, or length-limited functions (strncpy, snprintf). Enable compiler flags -fstack-protector-strong and -D_FORTIFY_SOURCE=2.
2. Use-After-Free
Accessing memory that has already been freed. Leads to reading/writing in another area or to arbitrary code execution.
// Vulnerable codeint* ptr = new int(42);delete ptr;*ptr = 100; // Error! ptr is no longer valid// Secure versionint* ptr = new int(42);delete ptr;ptr = nullptr; // Nullify the pointerif (ptr) *ptr = 100; // Check before useProtection: use smart pointers (std::unique_ptr, std::shared_ptr), nullify raw pointers after delete, apply sanitizers (-fsanitize=address).
3. Integer Overflow
Exceeding the allowable range during arithmetic operations. Often used to bypass buffer size checks.
// Vulnerable codesize_t size = 0;size_t user_input = 4294967295; // 2^32 - 1size_t total = size + user_input + 1; // Overflow!// Secure version with checkif (user_input > SIZE_MAX - size) { throw std::overflow_error("Size too large");}size_t total = size + user_input + 1;Protection: use fixed-width types (uint32_t), check for overflow before the operation, use __builtin_add_overflow (GCC/Clang).
4. Null Pointer Dereference
Attempting to access memory at address NULL. Leads to program crash (segfault) or a DoS vulnerability.
// Vulnerable codevoid process(int* ptr) { *ptr = 10; // If ptr == nullptr — crash}// Secure versionvoid process(int* ptr) { if (!ptr) { throw std::invalid_argument("Null pointer received"); } *ptr = 10;}Protection: always check pointers for nullptr before use, return std::optional instead of raw pointers.
5. Memory Leaks
Losing access to allocated memory without freeing it. Over time, it exhausts system resources.
// Vulnerable codevoid leak() { int* data = new int[100]; // Forgot delete[] data — leak}// Secure versionvoid no_leak() { std::unique_ptr<int[]> data = std::make_unique<int[]>(100); // Memory will be freed automatically}Protection: use RAII (smart pointers, containers