Main Content

Noexcept function might exit with an exception

Functions specified as noexcept, noexcept(true) or noexcept(<true condition>) exits with an exception, which causes abnormal termination of program execution, leading to resource leak and security vulnerability

Since R2020b

Description

This defect occurs when a callable entity that is specified by using noexcept, noexcept(true), or noexcept(<true condition>) might exit with an exception.

When a callable entity invokes other callable entities, Polyspace® makes certain assumptions to calculate whether there might be unhandled exceptions.

  • Functions: When a noexcept function calls another function, Polyspace checks whether the called function might raise an exception only if it is specified as noexcept(<false>).If the called function is specified as noexcept, Polyspace assumes that it does not raise an exception. Some standard library functions, such as the constructor of std::string, use pointers to functions to perform memory allocation, which might raise exceptions. Because these functions are not specified as noexcept(<false>), Polyspace does not flag a function that calls these standard library functions.

  • External function: When a noexcept function calls an external function, Polyspace flags the function declaration if the external function is specified as noexcept(<false>).

  • Virtual function: When a function calls a virtual function, Polyspace flags the function declaration if the virtual function is specified as noexcept(<false>) in a derived class. For instance, if a noexcept function calls a virtual function that is declared as noexcept(<true>) in the base class, and noexcept(<false>) in a subsequent derived class, Polyspace flags the declaration of the noexcept function.

  • Pointers to function: When a noexcept function invokes a pointer to a function, Polyspace assumes that the pointer to function does not raise exceptions.

When analyzing whether a function raises unhandled exceptions, Polyspace ignores:

  • Exceptions raised in destructors

  • Exceptions raised in atexit() operations

  • Compiler generated functions

Polyspace also ignores the dynamic context when checking for exceptions. For instance, a function might raise unhandled exceptions that arise only in certain dynamic contexts. Polyspace flags such a function even if the exception might not be raised.

Risk

You can specify that a callable entity does not exit with an exception by specifying it as noexcept, noexcept(true), or noexcept(<true condition>). The compiler omits the exception handing process for noexcept entities. When such an entity exits with an exception,the compiler implicitly invokes std::terminate().

Depending on the hardware and software you use, the function std::terminate() might invoke std::abort() to abnormally abort the execution without unwinding the stack. If the stack is not unwound before program termination, then the destructors of the variables in the stack are not invoked, leading to resource leak and security vulnerabilities.

Fix

Specify functions as noexcept or noexcept(true) only when you know the functions raise no exceptions. If you cannot determine the exception specification of a function, specify it by using noexcept(false)

Examples

expand all

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



void SpecFalseCT() noexcept 
{
	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 {         
	a.f();
}

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

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

void g() noexcept {               
	f();
}  

In this example, there are several noexcept functions. These functions invoke other callable entities like functions, external functions, and virtual functions.

  • Polyspace flags the declaration of the function template f_tp even though the throw statement is not reached because Polyspace ignores dynamic context. Polyspace also analyzes only the instantiated templates in your code. For instance, if f_tp is not instantiated in the function instantiate(), Polyspace does not analyze the template.

  • 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.

Correction – Specify Functions as noexcept(false)

You can modify your code so that the functions that might exit with an exception are specified as noexcept(false). Such functions can exit with an exceptions without abnormally terminating the program and this defect is not raised.

#include <stdexcept>
#include <typeinfo>
void LibraryFunc_noexcept_false() noexcept(false);
void SpecFalseCT() noexcept(false) 
{
	try {
		LibraryFunc_noexcept_false();
	} catch (int &e) {
		LibraryFunc_noexcept_false();  
	} catch (std::exception &e) {
	} catch (...) {
	}
}

The function SpecFalseCT() uses an external function that is specified as noexcept(false). The function SpecFalseCT() is specified as noexcept(false) because it might exit with an exception. This function does not raise the defect.

Correction – Handle Exceptions in noexcept Functions

You can modify your code so that exceptions are handled within a noexcept function. If raised exceptions and handled within the function, then the noexcept function does not exit with an exception and this defect is not raised.

#include <stdexcept>
#include <typeinfo>

void f() noexcept(false) { 
throw(2);
}
void CallerFunc() noexcept {          
	try {
		f();
	} catch (int i) {
	} catch (...) {
	}
}

The noexcept function CallerFunc() calls f() which can raise exceptions. CallerFunc() has handlers to handle exceptions that might be raised and does not exit with an exception. This functions does not raise the defect.

Correction – Justify Defects Using Comments

You can justify raised defects using comments: Because Polyspace analyzes functions statically, it might raise this defect to flag exceptions that are in dead code. Use comments to justify defects if you think the exception might not be raised.

#include <stdexcept>
#include <typeinfo>
void MightThrow(unsigned int input) noexcept{// polyspace DEFECT:NOEXCEPT_FUNCTION_THROWS [Justified:Unset] "Throw in dead code"
	if(input<0)
		throw 1;
	//..
	
}

The noexcept function MightThrow() exits with an exception in a dynamic context that might not arise. For instance, the unsigned int input is nonnegative and the throw statement does not execute. Because Polyspace analyzes functions statically, it raises the defect on the throw statement. Justify the defect using a comment. See Address Results in Polyspace User Interface Through Bug Fixes or Justifications.

Result Information

Group: C++ Exception
Language: C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: NOEXCEPT_FUNCTION_THROWS
Impact: High

Version History

Introduced in R2020b

expand all