Main Content

AUTOSAR C++14 Rule A15-5-2

Program shall not be abruptly terminated. In particular, an implicit or explicit invocation of std::abort(), std::quick_exit(), std::_Exit(), std::terminate() shall not be done

Since R2021b

Description

Rule Definition

Program shall not be abruptly terminated. In particular, an implicit or explicit invocation of std::abort(), std::quick_exit(), std::_Exit(), std::terminate() shall not be done.

Rationale

Functions such as std::abort(), std::quick_exit(), and std::_Exit() terminate the program immediately without invoking any exit handlers or calling any destructors for the constructed objects. The std::terminate() function implicitly calls std::abort() to terminate the program abruptly. Exceptions that are unhandled or cannot be handled might also cause abrupt termination of the program.

Depending on your environment, the compiler might not release the allocated resources and unwind the stack when the program is terminated abruptly, leading to issues such as memory leaks. Such abnormal program terminations might make the code vulnerable to denial-of-service attacks. Avoid terminating the program abruptly.

Polyspace Implementation

Polyspace® flags the operations that might result in abrupt termination of the program. For instance:

  • The destructor of a class exits with an unhandled exception. See AUTOSAR C++14 Rule A15-5-3.

  • The constructor of a global or a static object is invoked directly but it is not explicitly specified as noexcept. See AUTOSAR C++14 Rule A15-2-1.

  • A noexcept function raises an unhandled exception. See AUTOSAR C++14 Rule A15-4-2.

  • The argument of a throw statement raises an exception. See AUTOSAR C++14 Rule M15-1-1.

  • Unsafe termination functions such as std::_Exit, std::abort, and std::quick_exit are explicitly invoked.

  • The function std::terminate is explicitly invoked.

  • A handler for abnormal termination is explicitly registered by using the functions std::set_terminate or std::get_terminate.

  • A handler for normal termination that is registered to std::atexit raises an unhandled exception.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

This example shows how Polyspace flags noexcept functions that might raise unhandled exceptions. Consider this code containing several noexcept functions. These functions invoke other callable entities such as functions, external functions, and virtual functions.

#include <stdexcept>
#include <typeinfo>
void LibraryFunc(); 
void LibraryFunc_noexcept_false() noexcept(false);  
void LibraryFunc_noexcept_true() noexcept(true);    



void SpecFalseCT() noexcept  // Noncompliant
{
	try {
		LibraryFunc_noexcept_false();
	} catch (int &e) {
		LibraryFunc_noexcept_false();  
	} catch (std::exception &e) {
	} catch (...) {
	}
}

class A {
public:
	virtual void f() {}              
};

class B : A {
public:
	virtual void f() noexcept {}     
};

class C : B {
public:
	virtual void f() noexcept {}     
};

class D : A {
public:
	virtual void f() noexcept(false) { throw(2);}
};

void A1(A &a) noexcept {          // Noncompliant
	a.f();
}

void D2(D &d) noexcept {          //Compliant
	try {
		d.f();
	} catch (int i) {
	} catch (...) {
	}
}

void B2(B *b) noexcept {          // Compliant
	b->f();
}
template <class T>
T f_tp(T a) noexcept(sizeof(T)<=4)    // Noncompliant
{
	if (sizeof(T) >4 ) {
		throw std::runtime_error("invalid case");
	}
	return a;
}
void instantiate(void)
{
	f_tp<char>(1);
}
void f() noexcept {               //Noncompliant
	throw std::runtime_error("dead code");
}

void g() noexcept {               // Compliant
	f();
}  

  • Polyspace flags the declaration of the function template f_tp because:

    • The condition sizeof(T)<=4 evaluates to true for char so the template becomes a noexcept(true) function.

    • Polyspace analyzes the noexcept(true) instance of the template statically. Polyspace deduces that the template might raise an exception because of the throw statement, even though the condition sizeof(T)>4 is false. That is, Polyspace flags the template even though the throw statement is never reached.

    Polyspace ignores function templates that are not instantiated.

  • Polyspace flags the noexcept function SpecFaleCT() because this function calls the noexcept(false) external function LibraryFunc_noexcept_false() without encapsulating it in a try-catch block. Any exceptions raised by this call to the external function might raise an unhandled exception.

  • Polyspace flags the declaration of the noexcept function A1() because this function might call the noexcept(false) function D.f() when the input parameter a is of class D. Depending on the class of the input parameter, the noexcept polymorphic function A1() might raise an unhandled exception.

  • Polyspace flags the function f() because it is a noexcept function that uses throw to raise an unhandled exception. Polyspace does not flag the noexcept function g() even though it calls f() because f() is specified as noexcept.

  • Polyspace does not flag the noexcept function D2() even though it calls the noexcept(false) function D.f() because D2() handles the exceptions that might arise by using a catch(...) block.

This example shows how Polyspace flags the expressions in throw statements that can raise unexpected exceptions.

int f_throw() noexcept(false);

class WithDynamicAlloc {
public:
	WithDynamicAlloc(int n) {
		m_data = new int[n];   
	}
	~WithDynamicAlloc() {
		delete[] m_data;
	}
private:
	int* m_data;
};

class MightThrow {
public:
	MightThrow(bool b) {
		if (b) {
			throw 42;
		}
	}
};

class Base {
	virtual void bar() =0;
};
class Derived: public Base {
	void bar();
};
class UsingDerived {
public:
	UsingDerived(const Base& b) {
		m_d = 
		dynamic_cast<const Derived&>(b);
	}
private:
	Derived m_d;
};
class CopyThrows {
public:
	CopyThrows() noexcept(true);
	CopyThrows(const CopyThrows& other) noexcept(false);
};
int foo(){
	try{
		//...
		throw WithDynamicAlloc(10); //Noncompliant
		//...
		throw MightThrow(false);//Noncompliant
		throw MightThrow(true);//Noncompliant
		//...
		Derived d;
		throw  UsingDerived(d);// Noncompliant
		//... 
		throw f_throw(); //Noncompliant
		CopyThrows except;
		throw except;//Noncompliant
	}
	catch(WithDynamicAlloc& e){
		//... 
	}
	catch(MightThrow& e){
		//... 
	}
	catch(UsingDerived& e){
		//... 
	}
}

  • When constructing a WithDyamicAlloc object by calling the constructor WithDynamicAlloc(10), exceptions can be raised during dynamic memory allocation. Because the expression WithDynamicAlloc(10) can raise an exception, Polyspace flags the throw statement throw WithDynamicAlloc(10);

  • When constructing a UsingDerived object by calling the constructor UsingDervide(), exceptions can be raised during the dynamic casting operation. Because the expression UsingDerived(d) can raise exceptions, Polyspace flags the statement throw UsingDerived(d).

  • In the function MightThrow(), exceptions can be raised depending on the input to the function. Because Polyspace analyzes functions statically, it assumes that the function MightThrow() can raise exceptions. Polyspace flags the statements throw MightThrow(false) and throw MightThrow(true).

  • In the statement throw except, the object except is copied by implicitly calling the copy constructor of the class CopyThrows. Because the copy constructor is specified as noexcept(false), Polyspace assumes that the copy operation might raise exceptions. Polyspace flags the statement throw except.

  • Because the function f_throw() is specified as noexcept(false), Polyspace assumes that it can raise exceptions. Polyspace flags the statement throw f_throw().

#include<cstdlib>
class obj
{
public:
	obj() noexcept(false){}
	obj(const obj& a){/*...*/}
	~obj(){/*...*/}
};

void foo(){
	obj localObject;
	//...
	std::_Exit(-1);//Noncompliant
}
void bar(){
	obj localObject;
	//...
	std::abort;//Noncompliant
}
void foobar(){
	obj localObject;
	//...
	std::quick_exit(-1);//Noncompliant
}

In this example, unsafe termination functions are invoked to terminate the program. These functions do not perform the essential cleanup operations such as calling destructors. For instance, the destructors of the three instances of localObject are not invoked. Any resource allocated by these objects is leaked. Polyspace flags the use of such unsafe termination programs.

#include<cstdlib>
#include<exception>
class obj
{
public:
	obj() noexcept(false){}
	obj(const obj& a){/*...*/}
	~obj(){/*...*/}
};


void foobar(){
	obj localObject;
	//...
	std::terminate();//Noncompliant
}

Invoking std::terminate explicitly might result in abrupt termination of the program without calling the destructors of the local and static objects. Polyspace flags the explicit calls to std::terminate.

#include <stdexcept>
void atexit_handler(){//Noncompliant
	throw std::runtime_error("Error in atexit function");
}
void main(){
	try{
		//...
		std::atexit(atexit_handler);
	}catch(...){
		
	}
}

The termination handler atexit_handler raises an uncaught exception. The function atexit_handler executes after the main finishes execution. Unhandled exceptions in this function cannot be handled elsewhere, leading to an implicit call to std::terminate(). Polyspace flags the function.

#include <stdexcept>
#include <new>
class obj
{
public:
	obj() noexcept(false){}
	obj(const obj& a){
		//...
		throw -1;
	}
	~obj()
	{
		try{
			// ...
			throw std::runtime_error("Error2"); // Noncompliant
		}catch(std::bad_alloc& e){
			
		}
	}
};
obj globalObject; //Noncompliant

In this example, the constructor of the object globalObject is specified as noexcept. The destructor of the object explicitly raises an unhandled exception. These unhandled exception might arise before the execution starts or after the main function completes execution, which might result in an abrupt and unsafe termination by invoking std::abort(). Polyspace flags these operations.

Check Information

Group: Exception handling
Category: Required, Partially automated

Version History

Introduced in R2021b

expand all