Security of C++ Applications: Top 10 Vulnerabilities for Beginners
\n\nThe C++ language gives the developer enormous control over memory and performance, but this freedom also comes with great responsibility. According to OWASP and CWE, vulnerabilities in C++ applications often arise from improper memory management, lack of bounds checking, and carelessness when working with pointers. In this article, we will break down the 10 most common vulnerabilities that beginners encounter. You will learn how to detect them and, most importantly, how to avoid them.
\n\n1. Buffer Overflow
\nA classic of the genre. This vulnerability occurs when a program writes data beyond the boundaries of an allocated buffer. An attacker can overwrite adjacent memory areas, including the function's return address, and gain control over code execution.
\n\n// Vulnerable code\nvoid vulnerable() {\n char buffer[10];\n std::cout << "Enter a string: ";\n std::cin >> buffer; // No length check!\n std::cout << buffer << std::endl;\n}\n\n// Safe version\nvoid safe() {\n char buffer[10];\n std::cout << "Enter a string: ";\n std::cin.getline(buffer, sizeof(buffer));\n std::cout << buffer << std::endl;\n}\n\nHow to protect yourself: Always use functions with length limits (strncpy, snprintf, std::string). Avoid gets() and strcpy().
2. Memory Leaks
\nIf you allocate memory via new but forget to free it via delete, the program will gradually consume RAM. For server applications, this is a disaster — after a few hours of operation, the service will crash with a std::bad_alloc error.
// Example of a leak\nvoid leak() {\n int* ptr = new int(42);\n // Forgot delete ptr;\n}\n\n// Correct approach\nvoid noLeak() {\n std::unique_ptr<int> ptr = std::make_unique<int>(42);\n // Memory will be freed automatically when leaving scope\n}\n\nHow to protect yourself: Use smart pointers (std::unique_ptr, std::shared_ptr). As a last resort — RAII (Resource Acquisition Is Initialization).
3. Dangling Pointers
\nA pointer references already freed memory. Dereferencing such a pointer is undefined behavior (UB). This can lead to a program crash or, worse, to the execution of malicious code.
\n\nint* dangling() {\n int x = 10;\n return &x; // Error: returning address of a local variable!\n}\n\n// Correct — allocate memory on the heap\nint* safe() {\n int* ptr = new int(10);\n return ptr;\n}\n\nHow to protect yourself: After delete, always assign nullptr to the pointer. Use smart pointers.
4. Integer Overflow
\nWhen the result of an arithmetic operation exceeds the range of the data type, an overflow occurs. This can lead to unexpected consequences, such as allocating a buffer that is too small.
\n\nvoid check_size(size_t user_size) {\n // Vulnerability: if user_size is very large, user_size + 1 can become small\n char* buffer = new char[user_size + 1];\n // ...\n}\n\n// Protection\nvoid safe_check(size_t user_size) {\n if (user_size > std::numeric_limits<size_t>::max() - 1) {\n throw std::overflow_error("Size is too large");\n }\n char* buffer = new char[user_size + 1];\n}\n\nHow to protect yourself: Check boundaries before arithmetic operations. Use __builtin_add_overflow (GCC/Clang) or libraries for safe arithmetic.
5. Null Pointer Dereference
\nDereferencing a null poin