C/C++ bug classes #
Below is a list of common vulnerability types for C/C++ programs. This list does not include comprehensive details; rather, it is intended to be broad and high level. Use it as a starting point during security reviews of C/C++ programs.
- Buffer overflow and underflow, spatial safety
- Off-by-one mistakes
- Invalid computation of object sizes
- Misunderstanding of data-moving functions’ semantics
- Data comparison using out-of-bounds lengths
- Copying of raw memory instead of the object
- Out-of-bounds iterators
- Use after free, temporal safety
- Use after free
- Example: Two
shared_ptrs point to the same object, and one of them decrements its reference count (refcount) to 0 and frees the object. See this blog post for reference.
- Example: Two
- Use after scope, dangling pointers
- Example: Heap structures owning pointers to stack variables
- Use after return
- Example: With
return string("").c_str(), the string’s internal buffer is destroyed on return.
- Example: With
- Use after close
- Example: A file’s descriptor is saved in process memory, the file is closed, and then another file is assigned to the same descriptor. See this CTF challenge.
- Use after move
- Double free
- Misuse of smart pointers. See this example CTF writeup.
- Lambda capture issues
- Arbitrary pointer free
- Example: An attacker can call
freeon a pointer to memory that was not dynamically allocated or on data that is not a pointer.
- Example: An attacker can call
- Incorrect refcounts
- Example: A refcount is incremented when it should not be, or an object is not freed when its refcount drops to zero.
- Partial free
- Example: A struct’s field is freed but the struct is not, or vice versa.
- Misuse of memory-allocating library functions
- Example: OpenSSL’s
BN_CTX_startis called without a corresponding call toBN_CTX_end. See this blog post for reference.
- Example: OpenSSL’s
- Use after free
- Integer overflow, numeric errors
- Arithmetic overflows
- Results of computations do not fit in intermediate or final types.
- Widthness overflows
- Data is assigned to a too-small type.
- Signedness bugs
- Data is transformed in unexpected ways when its type’s sign changes.
- Implicit conversions
- The type of a variable changes unexpectedly.
- Negative assignment overflow
abs(INT_MIN) == INT_MIN == -2147483648int a = -b(ifb = INT_MIN, thena = b)
- Integer cut
- Example: The code reads
rax, compares onlyeax, and then usesrax.
- Example: The code reads
- Rounding errors
- Float imprecision
- Example: Direct comparison of floats without an epsilon
- Arithmetic overflows
- Type confusion, type safety issues
- Type confusion when casting
- Type confusion when deserializing
- Type confusion when dereferencing pointers (pointer to pointer instead of pointer)
- Void pointers
- Type safety issues related to unions
- Object slicing
- Variadic function misuse
- Format string bugs
- User input is used as the format string.
- Type mismatch bugs
- A format string specifier does not match the type of the provided argument.
- Format string bugs
- String issues
- Lack of null termination
- Issues related to
locale-dependent string operations
- When the execution environment may impact the logic of the code in unexpected ways
- Problems related to encoding and normalization (UTF-8, UTF-16, Unicode, etc.)
- Byte size not equal to character size
- Example: When multibyte or wide characters are used
- Use of uninitialized data
- Null pointer dereferences
- Unhandled errors
- Return values not checked
- Return values incorrectly compared
- Example: When a function returns 1 on success and 0 or negative on failure, but the code includes an
if (retval != 0)check
- Example: When a function returns 1 on success and 0 or negative on failure, but the code includes an
- Exception handling issues
- Memory leaks
- Uninitialized memory exposure
- Example: Via padding in structures
- Exposure of pointers
- Uninitialized memory exposure
- Initialization order bugs
- Example: Static initialization order fiasco
- Race conditions
- Time-of-check to time-of-use (TOCTOU)
- double fetch
- Over- or under-locking
- (Non-)thread-safe and signal-safe APIs
- Filesystem-related issues
- Issues with softlinks/symlinks
- Disk synchronization issues (fsyncing/flushing of data)
- Mishandling of unquoted paths (which may contain whitespace characters)
- Missing path separators (e.g.,
C:\app\filesvsC:\app\files\versusC:\app\files_sensitive) - Case sensitivity and normalization issues
- Predictable temporary files
- Iterator invalidation (see Trail of Bits’
blog post for reference)
- Accidental deletion of a list item while iterating over it
- Usage of error-prone functions
- See Intel’s SDL List of Banned Functions
- Denial of service
- High resource usage
- Leaks of resources, file descriptors, or memory
- Passing containers (e.g.,
vector) via value instead of via reference - Dangling references (e.g., after
moveor inlambdacaptures)
- Undefined behavior
- Invalid alignment
- Strict aliasing violation
- Signed integer overflow
- Shift by negative integer
- Shift by >= the type’s width
- And so many others…
- Compiler-introduced bugs
- Removal of security checks due to assumptions around undefined behavior
- Removal of null pointer checks and array bound checks due to null pointer dereference being UB
- Removal of integer overflow checks because signed integer overflow is UB
- Removal of data zeroization function calls
- Issues resulting from optimization of constant-time constructions
- Removal of debug assertions in production builds
- Removal of security checks due to assumptions around undefined behavior
- Operator precedence issues
- Problems with time
- Clocks may be non-monotonic.
- Time may change backward (time zones, daylight saving time, leap seconds).
- Access control issues
- Invalid regular expressions (regexes)
- ReDoS attacks possible
- Multi-line (newline) bypasses
- Lack of exploit mitigations
- Compile-time mitigations
- Runtime mitigations
- libc++ hardenings
- Typos in exploit mitigation configurations