Throw argument raises unexpected exception

Выражение аргумента в throw оператор вызывает неожиданные исключения, приводящие к утечкам ресурсов и уязвимостям безопасности

Описание

Этот дефект возникает, когда выражение аргумента оператора throw может вызвать исключение. Выражения, которые могут вызвать исключения, включают:

  • Функции, которые заданы как noexcept(false)

  • Функции, которые содержат одно или несколько явных throw операторы

  • Конструкторы, которые выполняют операции выделения памяти

  • Выражения, которые включают динамическое литье

Риск

При явном возникновении исключения при помощи throw операторы, компилятор сначала создает ожидаемое исключение путем оценки аргумента оператора throw, а затем поднимает ожидаемое исключение. Если при создании компилятором ожидаемого исключения в throw возникает непредвиденное исключение оператор, неожиданное исключение распространяется вместо ожидаемого. Это неожиданное исключение может стать необработанным исключением. В зависимости от вашего окружения компилятор может вызвать std::abort аномально прекратить выполнение программы без размотки стека, когда исключения становятся необработанными, что приводит к утечке ресурсов и уязвимостям безопасности. Рассмотрим этот код, где a throw оператор вызывает явное исключение класса myException.

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

foo(){
	try{
		//..
		throw myException();
	}
	catch(myException& e){
		//...
	}
}
При конструкции временных myException объект, new оператор может поднять bad_alloc исключение. В таком случае throw оператор поднимает bad_alloc исключение, вместо myException. Потому что myException было ожидаемым исключением, блок catch несовместим со bad_alloc. The bad_alloc исключение становится необработанным исключением. Это может привести к ненормальному прекращению работы программы без размотки стека, что приведет к утечке ресурсов и уязвимостям безопасности.

Зафиксировать

Избегайте использования выражений, которые могут вызвать исключения в качестве аргумента в throw оператор.

Примеры

расширить все

int f_throw() noexcept(false);
int foo(){
	try{
		//...
		throw f_throw();
	}
	catch(...){
		//...
	}
}

В этом примере функция f_throw() задается как noexcept(false). Если исключение вызвано в f_throw(), это может привести к тому, что программа завершится без размотки стека, что приведет к утечке ресурсов и уязвимостям безопасности.

Коррекция - используйте функции, заданные как noexcept(true) Как аргументное выражение throw Операторы

Одной из возможных коррекций является использование функций, которые не вызывают исключений в выражении аргумента throw оператор. Эти функции заданы как noexcept(true).

int f_throw() noexcept(true);
int foo(){
	try{
		//...
		throw f_throw();
	}
	catch(...){
		//...
	}
}
class WithDynamicAlloc {
public:
	WithDynamicAlloc(int n) {
		m_data = new int[n];   
	}
	~WithDynamicAlloc() {
		delete[] m_data;
	}
private:
	int* m_data;
};
int foo(){
	try{
		//...
		throw WithDynamicAlloc(10); 
	}
	catch(WithDynamicAlloc& e){
		//... 
	}
}

В этом примере конструктор объекта WithDynamicAlloc выполняет операцию динамического выделения памяти. Этот конструктор может вызвать исключение, такое как bad_alloc это может привести к завершению работы программы без размотки стека, что приведет к утечке ресурсов и уязвимостям безопасности.

Коррекция - используйте конструкторы, которые не выделяют память в качестве аргумента выражения throw Операторы

Одной из возможных коррекций является использование объектов, которые не требуют динамического выделения памяти, в качестве объектов исключений.

class WithoutDynamicAlloc {
public:
	WithoutDynamicAlloc(int n) : m_data(n){   
	}
	~WithoutDynamicAlloc() {
	}
private:
	int m_data;
};
int foo(){
	try{
		//...
		throw WithoutDynamicAlloc(10); 
	}
	catch(WithoutDynamicAlloc& e){
		//... 
	}
}
int MightThrow(bool b) {
	if (b) {
		throw 2.1;
	}
	return 42;
}
int foo(){
	try{
		//...
		throw MightThrow(false);
		throw MightThrow(true);
	}
	catch(int e){
		//... 
	}
}

В этом примере функция MightThrow() вызывает исключение в зависимости от входа b. Потому что Polyspace® анализирует функции статически, принимает, что MightThrow() вызывает исключения независимо от входа и вызывает нарушение этой проверки на обоих throw MightThrow(true) и throw MightThrow(false).

Коррекция - используйте комментарии для обоснования результата

Потому что MightThrow(false) не вызывает исключения, возможно коррекцию - использовать комментарии для обоснования оператора throw MightThrow(false).SeeAddress Результаты Polyspace через исправления ошибок или обоснования

int MightThrow(bool b) {
	if (b) {
		throw 2.1;
	}
	return 42;
}
int foo(){
	try{
		//...
		throw MightThrow(false);// polyspace DEFECT:THROW_THROWS [Justified:Low] "Does not Throw"
		throw MightThrow(true);
	}
	catch(int e){
		//... 
	}
}

Информация о результатах

Группа: Исключение C++
Язык: C++
По умолчанию: On для рукописного кода, off для сгенерированного кода
Синтаксис командной строки : THROW_ARGUMENT_EXPRESSION_THROWS
Влияние: Высокий
Введенный в R2020b