This vulnerability as described in ISO/IEC TR 24772-1:2019 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.
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:
// implicit
B* b_ptr = &d_inst; // implicit
C& c_ref = d_inst; static_cast<Z*>(&d_inst);
Z* z_ptr = dynamic_cast<Y*>(&d_inst); Y* y_ptr =
Downcasts:
dynamic_cast<D&>(*y_ptr);
D& d_ref = static_cast<D*>(b_ptr); D* d_ptr =
Crosscasts:
dynamic_cast<C*>(b_ptr);
C* c_ptr = dynamic_cast<Y*>(b_ptr);
Y* y_ptr2 = static_cast<C*> (static_cast<D*>(b_ptr)); C* c_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:
Follow the avoidance mechanisms of ISO/IEC 24772-1 clause 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; rely on proper virtual member functions in the base classes instead.
Avoid explicit upcast, 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. 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
.
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?