The memory leak vulnerability documented in ISO/IEC 24772-1:2024 clause 6.39 exists in C++, unless the programmer takes steps to avoid it. A leak occurs when an object whose lifetime (see 4.4) has not ended but there are no more variables, pointers or references to that object.
See ISO/IEC TR 24772-3 for the vulnerabilities associated with the C functions malloc()
, calloc()
, realloc()
and free()
.
C++ has an additional vulnerability in that it provides multiple alternatives for allocation and deallocation.
Failing to match the deallocation to the corresponding allocation results in undefined behaviour [EWF]. For example, if an array new[]
expression was used to allocate and create an array then array delete[]
must be used for its destruction and release to avoid undefined behaviour.
C++ destructors allow scope-based resource management which mitigates memory leaks by automatically calling the destructor when the lifetime of an object ends, thereby providing the opportunity to release their memory. The standard library provides container classes that follow the scope-based resource management idiom for the management of memory resources. Additionally, the standard library provides the class templates std::unique_ptr
and std::shared_ptr
for managing heap-allocated objects.
To avoid issues with constructors throwing exceptions during heap allocation with a new
expression and potentially causing leaks, these smart pointers should be obtained through the factory functions std::make_unique()
or std::make_shared()
. Using shared_ptr
can cause memory leaks if it is used to create a cyclic data structure.
If using functions that manage memory using the C library mechanisms is unavoidable, wrapping such a pointer immediately into a specialization of std::unique_ptr<>
that uses free()
in its deleter object ensures that memory is correctly released when the unique_ptr
is destroyed, for example:
struct free_deleter{
template <typename T>
void operator()(T *p) const {
std::free(const_cast<std::remove_const_t<T>*>(p));
}
};template <typename T>
using unique_C_ptr=std::unique_ptr<T,free_deleter>;
//...
// abi::__cxa_demangle() returns a pointer to be released with free()
inline auto plain_demangle(char const *name){
char const> result {abi::__cxa_demangle(name,0,0,0)};
unique_C_ptr<return result;
}
C++ allocators, i.e., as defined in header <scoped_allocator>
, can be used to mitigate heap fragmentation and guarantee deterministic timing through specific allocation strategies, especially with standard library containers. The class hierarchy provided by the header <memory_resource>
provide some possible advanced allocation strategies. Users of earlier C++ versions often overloaded operator new
and operator delete
to achieve similar results.
The library functions std::construct_at()
and std::destroy_at()
are simpler than and preferrable to using non-allocating placement new and manual destructor calls when those are needed.
To avoid the vulnerability or mitigate its ill effects, C++ software developers can:
Apply the avoidance mechanism of ISO/IEC 24772-1:2024 6.39.5
Refrain from using the C functions malloc()
, calloc()
, realloc()
and free()
wherever possible.
Prefer containers like those in the standard library over any other form of memory management.
Use smart pointers and their factory functions to allocate and manage heap memory.
For heap fragmentation issues, use special memory resource objects with appropriate allocation strategies.
If using std::shared_ptr
in potentially cyclic data structures, break cycles using std::weak_ptr
.
Use static analysis to prevent uses of C-library memory management functions and direct calls to operators new
and delete
.
Use dynamic analysis to detect memory leaks and issues with heap fragmentation.