メインコンテンツ

MISRA C++:2008 Rule 15-3-4

Each exception explicitly thrown in the code shall have a handler of a compatible type in all call paths that could lead to that point

説明

ルール定義

Each exception explicitly thrown in the code shall have a handler of a compatible type in all call paths that could lead to that point. 1

根拠

C++ では、操作で例外が発生すると、コンパイラが現在のスコープと隣接するスコープ内の互換性のある例外ハンドラーと例外を一致させようとします。発生した例外と互換性のある例外ハンドラーが存在しない場合は、コンパイラが暗黙的に関数 std::terminate() を呼び出します。関数 std::terminate() は処理系定義の方法でプログラム実行を終了します。つまり、プログラム終了の具体的なプロセスは、使用しているソフトウェアとハードウェアの特定の組み合わせに応じて異なります。たとえば、std::terminate()std::abort() を呼び出してスタックのアンワインドを行わずに実行を異常終了させる可能性があります。プログラムが終了する前にスタックがアンワインドされなかった場合は、スタック内の変数のデストラクターが呼び出されず、リソース リークやセキュリティの脆弱性につながります。

コードの try ブロック内で複数の例外が発生する次のコードについて考えます。

class General{/*...  */};
class Specific : public General{/*...*/};
class Different{}
void foo() noexcept
{
	try{
		//...
		throw(General e);
		//..
		throw( Specific e);
		// ...
		throw(Different e);
	}
	catch (General& b){

	}
}
コードの catch ブロックは、基底クラス General への参照を受け入れます。この catch ブロックは、基底クラス General とその派生クラス Specific の例外と互換性があります。クラス Different の例外と互換性のあるハンドラーは存在しません。この未処理例外は、このルールに違反しているため、リソース リークやセキュリティの脆弱性につながる可能性があります。

未処理例外はリソース リークやセキュリティの脆弱性につながる可能性があるため、コード内で明示的に発生した例外と互換性のあるハンドラーを一致させます。

Polyspace 実装

  • Polyspace® は、互換性のある catch ステートメントが関数の呼び出しパス内に存在しない場合に、関数内の throw ステートメントにフラグを設定します。関数が noexcept として指定されていない場合は、Polyspace がその呼び出しパスから main() などのエントリ ポイントが欠落している場合にその関数を無視します。

  • Polyspace は、catch(…) ステートメントを使用して発生した例外を処理する throw ステートメントにフラグを設定します。

  • Polyspace は、再スロー ステートメント、つまり、catch ブロック内の throw ステートメントにはフラグを設定しません。

  • 関数の入れ子になった try-catch ブロック内に throw ステートメント用の互換性のある catch ブロックがあっても、Polyspace は入れ子になった try-catch ブロックを無視します。コメントを使用して、入れ子構造内に互換性のある catch ブロックが入っている throw ステートメントを正当化します。または、関数で単一レベルの try-catch を使用します。

トラブルシューティング

ルール違反が想定されるものの、Polyspace から報告されない場合は、コーディング規約違反が想定どおりに表示されない理由の診断を参照してください。

すべて展開する

この例では、Polyspace が互換性のあるハンドラーを使用せずに例外が発生する操作にフラグを設定する様子を示します。次のコードについて考えます。

#include <stdexcept>

class MyException : public std::runtime_error {
public:
	MyException() : std::runtime_error("MyException") {}
};

void ThrowingFunc() {
	throw MyException(); //Noncompliant
}

void CompliantCaller() {
	try {
		ThrowingFunc();
	} catch (std::exception& e) {
		/* ... */
	}
}

void NoncompliantCaller() {
	ThrowingFunc(); 
}

int main(void) {
	CompliantCaller();
	NoncompliantCaller(); 
}

void GenericHandler() {
	try {
		throw MyException(); //Noncompliant
	} catch (...) { 
		/* ... */
	}
}

void TrueNoexcept() noexcept {
	try {
		throw MyException();//Compliant
	} catch (std::exception& e) {
		/* ... */
	}
}

void NotNoexcept() noexcept {
	try {
		throw MyException(); //Noncompliant
	} catch (std::logic_error& e) {
		/* ... */
	}
} 
  • 関数 ThrowingFunc() で例外が発生します。この関数には複数の呼び出しパスがあります。

    • main()->CompliantCaller()->ThrowingFunc():この呼び出しパスでは、関数 CompliantCaller()ThrowingFunc() によって発生した例外と互換性のある catch ブロックが存在します。この呼び出しパスはこのルールに準拠しています。

    • main()->NoncompliantCaller()->ThrowingFunc():この呼び出しパスでは、ThrowingFunc() によって発生した例外用の互換性のあるハンドラーが存在しません。Polyspace は、ThrowingFunc() 内の throw ステートメントにフラグを設定し、コード内の呼び出しパスを強調表示します。

    main() 関数は、この両方の呼び出しパスのエントリ ポイントです。main() がコメント アウトされている場合は、Polyspace がその両方の呼び出しパスを無視します。エントリ ポイントが欠落している呼び出しパスを解析するには、最上位の呼び出し関数を noexcept として指定します。

  • 関数 GenericHandler() は、throw ステートメントを使用することによって例外を発生させ、発生した例外を汎用の catch-all ブロックを使用して処理します。Polyspace はこのような catch-all ハンドラーが明示的な throw ステートメントによって発生する例外と互換性がないと見なすため、Polyspace は GenericHandler() 内の throw ステートメントにフラグを設定します。

  • 関数 noexcept TrueNoexcept() には、明示的な throw ステートメントと互換性のあるタイプの catch ブックが含まれています。この throw ステートメントは互換性のある catch ブロックと一致するため、このルールに準拠しています。

  • 関数 noexcept NotNoexcept() には、明示的な throw ステートメントが含まれていますが、catch ブロックには発生する例外と互換性がありません。この throw ステートメントは互換性のある catch ブロックと一致しないため、Polyspace は、NotNoexcept() 内の throw ステートメントにフラグを設定します。

チェック情報

グループ: Exception Handling
カテゴリ: 必要

バージョン履歴

R2020b で導入


1 All MISRA coding rules and directives are © Copyright The MISRA Consortium Limited 2021.

The MISRA coding standards referenced in the Polyspace Bug Finder™ documentation are from the following MISRA standards:

  • MISRA C:2004

  • MISRA C:2012

  • MISRA C:2023

  • MISRA C++:2008

  • MISRA C++:2023

MISRA and MISRA C are registered trademarks of The MISRA Consortium Limited 2021.