Main Content

CERT C++: DCL56-CPP

Avoid cycles during initialization of static objects

Since R2022b

Description

Rule Definition

Avoid cycles during initialization of static objects.1

Polyspace Implementation

The rule checker checks for these issues:

  • Recursive initialization of static variables

  • Undetermined initialization order of global variables

Examples

expand all

Issue

This issue occurs when you declare a static variable in a function and initialize the static variable by calling the same function.

int foo(int) {
    static int var = foo(0);
    //...
}

Risk

Recursively initializing a static variable might result in undefined behavior. The code does not need to be infinitely recursive for undefined behavior to occur.

Fix

Avoid recursive calls when initializing variables. For example, use zero-initialization at the declaration of a static variable, then compute the values.

Example

In the following example, the behavior is undefined because the initialization of the static variable buffer is within the fibonacci function, and the initialization of buffer requires reentering the same function fibonacci.

#include <stdexcept>

int fibonacci(int i) noexcept(false)
{
    if (i < 0) {
        //no negative fibonacci number
        throw std::domain_error("Input must be nonegative");
    }
    static const int buffer[] = { fibonacci(0), fibonacci(1),  //Noncompliant
        fibonacci(2), fibonacci(3), fibonacci(4), fibonacci(5), 
        fibonacci(6), fibonacci(7) }; 

    if (i < (sizeof(buffer) / sizeof(int))) {
        return buffer[i];
    }

    return i > 0 ? fibonacci(i - 1) + fibonacci(i - 2) : 1;
}

The fibonacci function cannot calculate the next value until computation of the previous value is complete.

Correction

Use zero-initialization to determine if every member of the array an assigned value. If not, recursively compute the value.

#include <stdexcept>

int fibonacci(int i) noexcept(false)
{
	if (i < 0) {
		//no negative fibonacci number
		throw std::domain_error("Input must be nonegative");
	}

	static int buffer[7]; // default zero-initailization

	if (i < sizeof(buffer) / sizeof(int))
	{
		if (0 == buffer[i])
		{
			buffer[i] = i > 0 ? fibonacci(i - 1) + fibonacci(i - 2) : 1;
		}
		return buffer[i];
	}

}
Issue

This issue occurs when a global variable initialization in a file depends on global variables that are initialized in another file.

Risk

If a static variable is used before it is initialized, it may cause unspecified behavior.

Fix

Verify that global objects are initialized before their first use.

Example

In this example, the file aSource.cpp contains the variable int tacos. The value of int tacos relies on the initialization of TacoOrder t. However, TacoOrder t might not be initialized by get_default_taco() in the source file otherSource.cpp before t.get_num_tacos() is called inside the source file aSource.cpp.

aHeader.h:

class TacoOrder {
	int numTacos;

public:
	TacoOrder() : numTacos(2) {}
	explicit TacoOrder(int numTacos) : numTacos(numTacos) {}

	int get_num_tacos() const { return numTacos; }
};

aSource.cpp:

#include "aHeader.h"
#include <iostream>

extern TacoOrder t;
int tacos = t.get_num_tacos();  //Noncompliant

void main()
{
	std::cout << tacos << std::endl;
}

otherSource.cpp:

#include "aHeader.h"

TacoOrder get_default_taco() { return TacoOrder(6); }
TacoOrder t = get_default_taco();

 
Correction

In this example, the only file that needs to be changed is aSource.cpp.

aHeader.h:

class TacoOrder {
	int numTacos;

public:
	TacoOrder() : numTacos(2) {}
	explicit TacoOrder(int numTacos) : numTacos(numTacos) {}

	int get_num_tacos() const { return numTacos; }
};

aSource.cpp:

#include "aHeader.h"
#include <iostream>

int &get_num_tacos() {
	extern TacoOrder t;
	static int tacos = t.get_num_tacos();
	return tacos;
}

void main()
{
	std::cout << get_num_tacos << std::endl;
}

otherSource.cpp:

#include "aHeader.h"

TacoOrder get_default_taco() { return TacoOrder(6); }
TacoOrder t = get_default_taco();

 

The initialization of tacos happens after the declaration when you move the static int tacos object inside the body of a function. TacoOrder t is initialized by get_default_taco() in source file otherSource.cpp by the time get_num_tacos() is called inside of main().

Check Information

Group: Rule 01. Declarations and Initialization (DCL)

Version History

Introduced in R2022b


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.