Security of C++ Applications: Top 10 Vulnerabilities in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Security of C++ Applications: Top 10 Vulnerabilities in Practice



C++ is a powerful and high-performance language, but its flexibility requires a high degree of responsibility from the developer. Errors in memory management, thread handling, or input/output can lead to serious vulnerabilities. In this article, we will analyze the top 10 C++ vulnerabilities with practical code examples and ways to prevent them. The material will be useful for both beginners and experienced developers looking to strengthen the security of their projects.



1. Buffer Overflow

A classic vulnerability that occurs when data is written beyond the boundaries of an allocated array. In C++, it is especially dangerous when working with raw arrays and C functions.



#include <iostream>#include <cstring>

void vulnerable(char* input) { char buffer[10]; strcpy(buffer, input); // DANGEROUS: no length check std::cout << "Buffer: " << buffer << std::endl;}

int main() { char longInput[] = "AAAAAAAABBBBBBBBCCCCCCCC"; // 24 characters vulnerable(longInput); return 0;}


Solution: Use std::string or strncpy with an explicit length check. In modern C++, STL containers are preferred.



void safe(const std::string& input) {    std::string buffer = input.substr(0, 9);  // safe copying    std::cout << "Buffer: " << buffer << std::endl;}


2. Memory Leaks

Leaks occur when dynamically allocated memory is not freed. In long-running applications, this leads to resource exhaustion.



void leakExample() {    int* data = new int[1000];  // allocate memory    // ... work with data ...    // forgot delete[] data; — leak!}


Solution: Use smart pointers (std::unique_ptr, std::shared_ptr) or STL containers.



void safeExample() {    std::unique_ptr<int[]> data = std::make_unique<int[]>(1000);    // memory is automatically freed when leaving the function}


3. Null Pointer Dereference

Attempting to access memory at address nullptr causes undefined behavior and often leads to a program crash.



void process(int* ptr) {    *ptr = 42;  // DANGEROUS: if ptr == nullptr}

int main() { int* p = nullptr; process(p); // crash}


Solution: Always check pointers before use. Use references if the value cannot be null.



void safeProcess(int* ptr) {    if (ptr == nullptr) {        throw std::invalid_argument("Null pointer received");    }    *ptr = 42;}


4. Race Condition

Occurs when multiple threads access shared data simultaneously without synchronization.



#include <thread>int counter = 0;

void increment() { for (int i = 0; i < 100000; ++i) { counter++; // non-atomic operation — race condition }}

int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Counter: " << counter; // unpredictable result}


Solution: Use mutexes or atomic operations.



#include <atomic>std::atomic<int> counter{0};

void safeIncrement() { for (int i = 0; i < 100000; ++i) { counter.fetch_add(1); // atomic operation }}


5. Use of Uninitialized Variables

Uninitialized variables contain "garbage," which can lead to unpredictable behavior.



int main() {    in                

Blogs

Book Recommendations