This vulnerability as documented in ISO/IEC 24772-1:2024, 6.44 applies to C++. In addition to the upcast and downcast issues addressed in that document, this clause also addresses cross-casting, which is unique to C++. For further type system related issues see subclause Type System[IHN].
C++ provides language mitigations to help avoid the problems as follows:
Since C++ supports multiple inheritance, up-casting, down-casting, and cross-casting operations can be used to switch to different (pointer/reference) types in the inheritance hierarchy of a specific object, i.e.,
up-casting is casting an object to an ancestor type in the object's type inheritance hierarchy.
down-casting is casting an object to a descendent type in the object's type inheritance hierarchy, and,
cross-casting is casting an object to a sibling/cousin (possibly removed) type in the object's type inheritance hierarchy with multiple inheritance.
Unsafe casts, which include C-style casts and reinterpret_cast, can cast to unrelated arbitrarily structured types. This allows reading and modifying arbitrary memory areas. See subclause [[6.11 Pointer Casting and Pointer Type Changes] [HFC](#HFC) for more details.
In addition, developers should be aware that virtual member functions can be overridden in derived classes, even if they are private.
Given the following:
struct Z { int z; virtual ~Z() { } };
struct Y { int y; virtual ~Y() { } };
struct A : Z { int a; };
struct B : virtual A { int b; };
struct C : virtual A, Y { int c; };
struct D : B, C { int d; };
D d_inst;then these examples demonstrate upcasts, downcasts, and crosscasts:
Upcasts:
B* b_ptr = &d_inst; // implicit
C& c_ref = d_inst; // implicit
Z* z_ptr = static_cast<Z*>(&d_inst);
Y* y_ptr = dynamic_cast<Y*>(&d_inst);Downcasts:
D& d_ref = dynamic_cast<D&>(*y_ptr);
D* d_ptr = static_cast<D*>(b_ptr);Crosscasts:
C* c_ptr = dynamic_cast<C*>(b_ptr);
Y* y_ptr2 = dynamic_cast<Y*>(b_ptr);
C* c_ptr = static_cast<C*> (static_cast<D*>(b_ptr));and notes the following about such:
Upcasts:
are the only ones that can be performed implicitly
can cause object slicing when a copy of a base class object is created from a derived class object.
can also be done with dynamic_cast or static_cast
Downcasts
are explicit;
can be done safely with dynamic_cast
dynamic_cast requires appropriate portions of inheritance to be polymorphic (i.e. has virtual members);
can be done using static_cast which is unchecked and may be unsafe;
Crosscasts:
are explicit
can be done safely with a single call to dynamic_cast which requires appropriate portions of inheritance to be polymorphic (i.e. has virtual members).
can often be done with a chain of static_casts traversing the inheritance hierarchy, which is almost always unsafe.
Deleting derived objects via a base class pointer is undefined behavior [EWF], unless the base class declares a virtual destructor.
To avoid the vulnerability or mitigate its ill effects, C++ software developers can:
apply the avoidance mechanisms of ISO/IEC 24772-1:2024, 6.44.5;
in a base class declaring virtual member functions, define a virtual defaulted destructor (Core Guidelines C.35) and make the class non-copyable (Core Guidelines C.130);
avoid designs relying on downcasts or crosscasts and rely on proper virtual member functions in the base classes instead;
avoid explicit upcast and rely on implicit conversion;
if a downcast or a crosscast is needed, prefer using dynamic_cast since it is checked;
ensure that all invariants of a derived class are preserved by all public operations on its public base classes and, if this cannot be ensured, make the base class private, or avoid inheritance;
avoid attempting to navigate class hierarchies using C-style casts or reinterpret_cast; and
prohibit deletion of a polymorphic object without a virtual destructor, including using std::unique_ptr<base> from a unique pointer allocated with std::make_unique<derived>().
See also C++ Core Guidelines ES.48, ES.49, C.146, C.147, C.148 and C.153. source: OOP52-CPP?