parts/6.39.MemoryLeakAndHeapFragmentation-XYL.md

6.39 Memory Leak and Heap Fragmentation [XYL]

6.39.1 Applicability to language

The memory leak vulnerability documented in ISO/IEC TR24772-1:2019 clause 6.39 exists in C++, unless the programmer takes steps to avoid it. Using standard library containers sidesteps most memory leak issues described in that document.

See ISO/IEC TR 24772-3 for issues associated with the C functions malloc(), calloc(), realloc() and free(). Because of the issues with these functions, C++ users should refrain from using these functions wherever possible.

C++ has an additional vulnerability in that it provides multiple alternatives for allocation and deallocation.

Failing to match the deallocation to the corresponding allocation causes undefined behaviour. 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.

The C++ object lifetime model allows to create an object in existing raw memory using non-allocating placement new. Such an object must be destroyed by calling its destructor explicitly, a call to delete causes undefined behaviour.

C++ destructors allow scope-based resource management that should be used to mitigate memory leaks. 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){
  unique_C_ptr<char const> result {abi::__cxa_demangle(name,0,0,0)};
  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.

6.39.2 Avoidance mechanisms for language users

To avoid the vulnerability or mitigate its ill effects, C++ software developers can:

C++ software developers can avoid the vulnerability or mitigate its ill effects in the following ways. They can: