parts/6.11.PointerTypeConversion-HFC.md

6.11 Pointer Type Conversions [HFC]

6.11.1 Applicability to language

The vulnerabilites as described in ISO/IEC TR 24772-1:2019 clause 6.11.1 applies to C++. In addition to pointers, C++ references are also vulnerable and the issues below include references when pointers are mentioned. In places where references cannot be substituted the corresponding code won’t compile.

In general casting pointers breaks the type system and should be avoided.

In C++, a C-style cast is defined in terms of the C++ cast operators const_cast, static_cast, and reinterpret_cast. In some cases, it is unspecified which cast is used, for example, when a cast operation involves an incomplete type, a reinterpret_cast may be used for the conversion which can produce an incorrect result.

Especially, reinterpret_cast has the problem that it takes the original pointer value as a pointer of the target type rather than the original type. The C++ standard defines most cases where that happens as undefined behavior. For example, the lifetime model of C++ might result in accessing the target type object outside of its lifetime. Other run-time issues can be caused by alignment violations. Using reinterpret_cast<std::byte*> to access the underlying memory of an object by casting its address permits access to the raw memory. However, casting the address of a piece of raw memory with the correct alignment and size to an object pointer and accessing that object is undefined behavior for most types, because doing so, will not start the lifetime of the object.

static_cast only works, where conversion of the source type to the target type are related. However, with pointer types the compiler cannot always check that the actual object type corresponds to the desired target type, causing invalid casts. Naïvely assuming that addresses of a derived object and its base object are identical is wrong in most cases. For example, with multiple inheritance, the address of an object may be different than one of its base class sub-objects. Using the generic pointer type void* (which is common in C APIs) allows converting between arbitrary pointer types using static_cast. Most conversions via void * where the originial object type and the final target type are different are undefined behavior in C++. C++ allows reinterpret_cast to a pointer to an incomplete type or a static_cast from void * to a pointer to an incomplete type. Pointers to objects can implicitly convert to void * (cv-qualified accordingly).

It is only defined to reinterpret cast the obtained pointer back to the original type. It is implementation-defined if that bidirectional void * conversion also works for function pointers. A reinterpret cast can be used to convert a pointer from the integral types std::uintptr_t/std::intptr_t, but only if the value of the integer value was previously obtained by converting a valid pointer to said integral type. Casting an arbitrary integral value to a pointer is undefined behavior.

Casting along the inheritance relationships with dynamic_cast is safe, but it requires the dynamic type is known, which is the case when the types declare virtual member functions. Within a constructor or destructor only the static type of the current class is relevant, because the lifetime of any derived class object hasn’t started or has already ended. See subclause Polymorphic Variables [BKK].

Conversions involving const and/or volatile properties of a type are permitted using const_cast (see Modifying constants [UJO]). Adding const with const_cast is safe.

6.11.2 Avoidance mechanisms for language users

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