Main Content

AUTOSAR C++14 Rule M15-1-1

The assignment-expression of a throw statement shall not itself cause an exception to be thrown

Since R2020b

Description

Rule Definition

The assignment-expression of a throw statement shall not itself cause an exception to be thrown.

Rationale

In C++, you can use a throw statement to raise exceptions explicitly. The compiler executes such a throw statement in two steps:

  • First, it creates the argument for the throw statement. The compiler might call a constructor or evaluate an assignment expression to create the argument object.

  • Then, it raises the created object as an exception. The compiler tries to match the exception object to a compatible handler.

If an unexpected exception is raised when the compiler is creating the expected exception in a throw statement, the unexpected exception is raised instead of the expected one. Consider this code where a throw statement raises an explicit exception of class myException.

class myException{
	myException(){
		msg = new char[10];
		//...
	}
	//...
};

foo(){
	try{
		//..
		throw myException();
	}
	catch(myException& e){
		//...
	}
}
During construction of the temporary myException object, the new operator can raise a bad_alloc exception. In such a case, the throw statement raises a bad_alloc exception instead of myException. Because myException was the expected exception, the catch block is incompatible with bad_alloc. The bad_alloc exception becomes an unhandled exception. It might cause the program to abort abnormally without unwinding the stack, leading to resource leak and security vulnerabilities.

Unexpected exceptions arising from the argument of a throw statement can cause resource leaks and security vulnerabilities. To prevent such unwanted outcome, avoid using expressions that might raise exceptions as argument in a throw statement.

Polyspace Implementation

Polyspace® flags the expressions in throw statements that can raise an exception. Expressions that can raise exceptions can include:

  • Functions that are specified as noexcept(false)

  • Functions that contain one or more explicit throw statements

  • Constructors that perform memory allocation operations

  • Expressions that involve dynamic casting

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 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().

Check Information

Group: Exception handling
Category: Required, Automated

Version History

Introduced in R2020b