parts/6.14.DanglingReferenceToHeap-XYK.md

6.14 Dangling Reference to Heap [XYK]

6.14.1 Applicability to language

The vulnerability as expressed in ISO/IEC TR 24772-1:2019 and ISO/IEC TR 24772-3:2020 C exists in C++. C++, however, provides mechanisms to mitigate the vulnerability. In contrast to C, where the mere existance of reachable memory for an object is sufficient to access it, the lifetime model of C++ makes it undefined behaviour (see subclause [EWF]) to access an object outside of its lifetime. This results in undefined behavior, when an object access is attempted before one of its constructors is finished or after its destruction. For example, container types like std::vector or wrapper types like std::optional might have memory for an object available, that is not constructed or has ended its lifetime. For similar situations that result from accessing temporary objects or variables outside of their lifetime see subclause [DCM]. If such a temporary or local object manages heap memory (e.g., std::vector) referring to an element after the manager’s lifetime ended technically falls into the category of this vulnerability, but is covered there.

C++ provides a rich set of pointer-like types (potentially referring to heap memory) whose values may dangle, e.g.,

In addition, a user-defined class type can be a pointer-like type, if a subobject is of pointer-like type and refers to an object (target) whose lifetime is different from and not managed by the current object. Sometimes, regular object types act as pointer-like types, e.g., indices into a container or operating system handles, and their validity can not be directly mapped to the C++ object lifetime model.

If the lifetime of a pointer-like value ends before the lifetime of its target, then the vulnerability does not apply to that pointer-like value. This is the primary C++ strategy for avoiding vulnerabilities of dangling pointer-like values. For example, an object argument passed as a function parameter of reference type persists throughout the function call. The lifetime guarantee of a function argument passed indirectly via a pointer-like type does not apply if * the target is destroyed explicitly by the called function (taking ownership of the target) or a concurrently executing operation, or if * copies of the pointer-like parameter outlive the function call, for example, as the return value, or in a coroutine or thread frame.

For objects directly allocated on the heap C++ provides smart pointers and corresponding factory functions (e.g., std::make_unique()) that allow transferring ownership or shared ownership to reduce the risk for dangling. However, storing the raw pointers managed by smart pointers can lead to accidental dangling, for example:

int * f(){
    auto up = std::make_unique<int>(42);
    return up.get(); // returned pointer dangles
}

The C++ library containers, such as std::vector, manage the required heap memory for their elements. Referring to an element in a container via a pointer-like type is safe, as long as the container remains unchanged while the element object is accessed. In general, accessing an element in a mutated container via a pointer-like value obtained before the mutation is undefined behavior. Different containers provide different validity guarantees of accessing an element via a pointer-like type that was obtained before a subsequent change in that container.

Hand-written loops are prone to attempt to access elements of a container that are non-existent, or have been relocated. Employing standard library algorithms to iterate over a range of elements from a container tends to be safer, as long as the underlying container is not accidentally changed. For example, the following code can cause a failure, due to the attempt to iterate over a changing std::vector:

std::vector v{1,2,3,4};
copy(begin(v),end(v),back_inserter(v)); // modifying v while iterating is undefined behaviour
copy(begin(v),end(v), std::ostream_iterator<int>(std::cout,", "));

6.14.2 Avoidance mechanisms for language users

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