parts/6.22.InitializationOfVariables-LAV.md

6.22 Missing Initialization of Variables [LAV]

6.22.1 Applicability to language

The vulnerability as described in ISO/IEC TR 24772-1:2019 exists in C++, however, defining variables only when they can be initialized properly with an initializer in their definition avoids reading uninitialized memory.

Defining/allocating objects of trivial type with automatic/dynamic storage duration without initialization leaves the object with an indeterminate value. A subsequent read of such a variable before it has been written is undefined behavior. In addition, sub-objects of trivial type that are omitted in a constructor’s member initializer list and not initialized by the constructor’s body or by a default member initializer will not be initialized by that constructor. For example, the following class definitions suffer from incomplete initialization of subobjects, even though class test defines a default constructor:

struct base { short num; };                                                     
struct test : base                                                              
{                                                                               
  enum E1 { e1a=100, e1b, e1c };                                                
  int one; 
  int two; 
  double ar1[2]{ 1.1, 2.2 }; 
  double ar2[2]; 
  E1 e1; 
  E1 e2;                 
  test() 
  : // base unintialized 
  one{ 1 }
  // two uninitialized
  // ar1 initialized through default member initializer
  // ar2 uninitialized
  , e1{} // initializes to zero, not a named enumerator value 
  // e2 uninitialized
  { }                                  
};                                                                              

Dynamically allocating memory for an object using malloc, or some other C-style equivalent, does not initialize the object. Interpreting such memory as an object with trivial type will result in it having an indeterminate value.
Objects with non-trivial type require running a constructor for its lifetime to start correctly. In both cases attempting to cast a pointer to the allocated memory and using the object is undefined behavior except for special sanctioned cases, see Conversion Errors [FLC] and Pointer Type Conversion [HFC].

Non-local variables with static storage duration that are dynamically initialized can cause undefined behavior if the initialization depends on other such variables. If the dependency is in the same translation unit the sequencing is defined in definition order, however, there is no sequencing guarantees across translation unit boundaries and thus, undefined behaviour can occur by accessing an uninitialized variable. For example:

struct A {
  A (int i ) : i_ { i }  {  }
  int i_;
};
struct B {
  B (A const & a) : j_{a.i_} { }
  int j_;
};
extern A a;  // declare existance of variable 'a'
// defining variables with dynamic intialization:
B b { a };   //  #1
A a { 42 };  //  #2

If #1 and #2 are in the same translation unit, then a in #1 is incompletely initialized (zero initialized). If #1 and #2 are in different translation units, then the order of initialization of a(#2) relative to b(#1) is indeterminate.

The constexpr-specifier for a variable ensures initialization at compile time. The constinit specifier ensures a variable is initialized at compile time, even if it is non-const.

Defining non-member variables as const or as constexpr, enforces initialization by the compiler and makes reasoning about code easier.

If determining the initial value of a variable requires complex logic, putting that logic into an immediately-invoked lambda expression that computes the initial value, permits the variable to be initialized when defined.

See C++ Core Guidelines ES.20 and CERT C++ Coding Guidelines EXP53-CPP. Note that ES.20 and EXP53 are complementary. Both point out that you should always initialize before reading, but ES.20 uses the narrow sense of initialize while EXP53 includes assignment.

6.22.2 Avoidance mechanisms for language users

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