メインコンテンツ

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

Exceptions shall be raised only after startup and before termination of the program

説明

ルール定義

Exceptions shall be raised only after startup and before termination of the program. 1

根拠

C++ では、例外処理のプロセスが main() の実行中に発生します。ここでは、複数の異なるスコープで発生した例外が同じまたは隣接するスコープ内の例外ハンドラーによって処理されます。main() の実行の開始前は、コンパイラは起動フェーズにあり、main() の実行の終了後に、終了フェーズに移行します。この 2 つのフェーズ中に、コンパイラは、一連の事前に定義された操作を実行しますが、コードは実行しません。

起動フェーズと終了フェーズのどちらかで例外が発生する場合は、そのようなフェーズでコンパイラにより実行可能な例外ハンドラーを記述することはできません。たとえば、例外を処理するため、main()function-try-catch ブロックとして実装できます。main() 内の catch ブロックは、main() で発生した例外しか処理できません。起動または終了フェーズ中に発生した例外を処理できる catch ブロックは存在しません。このような例外が発生した場合は、コンパイラがスタックをアンワインドせずにコードの実行を異常終了する可能性があります。静的オブジェクト obj の構築と破棄で例外が発生する可能性がある次のコードについて考えます。

class A{
	A(){throw(0);}
	~A(){throw(0)}	
};

static A obj;

main(){
	//...
}
静的オブジェクト obj は、main() の開始前に A() を呼び出すことによって構築され、main() の終了後に ~A() を呼び出すことによって破棄されます。A() または ~A() で例外が発生した場合は、それに対応可能な例外ハンドラーが存在しません。実装に基づいて、このような例外はスタックをアンワインドせずにプログラムを終了させる可能性があり、メモリ リークやセキュリティの脆弱性につながります。

プログラムの起動前または終了後に実行される可能性があるコードの一部で、例外を発生させる操作を避けます。たとえば、静的オブジェクトおよびグローバル オブジェクトのコンストラクターおよびデストラクターで例外を発生させる可能性がある操作を避けます。

Polyspace 実装

Polyspace® は、例外を発生させる可能性のある呼び出し可能エンティティを使用するグローバルまたは静的変数宣言にフラグを設定します。次に例を示します。

  • 関数: 初期化子関数またはコンストラクターを直接呼び出してグローバル変数または静的変数を初期化するとき、Polyspace はその関数で例外が発生するかどうかをチェックして、関数で例外が発生する可能性がある場合は変数宣言にフラグを設定します。Polyspace は関数の例外指定にかかわらず、その関数で例外が発生する可能性があるかどうかを推定します。たとえば、noexcept コンストラクターによって例外が発生する場合、Polyspace はフラグを設定します。初期化子またはコンストラクターが別の関数を呼び出すと、Polyspace は、呼び出された関数が noexcept(<false>) として指定されている場合にのみ、例外を発生させる可能性があると仮定します。std::string のコンストラクターなど、一部の標準ライブラリ関数では、メモリ割り当てを実行するために関数へのポインターが使用され、これが例外を発生させる可能性があります。Polyspace はこれらの関数が使用されるときの変数宣言にはフラグを設定しません。

  • 外部関数:外部関数を呼び出してグローバル変数または静的変数を初期化するとき、外部関数が noexcept(<false>) として指定されている場合、Polyspace は宣言にフラグを設定します。

  • バーチャル関数:バーチャル関数を呼び出してグローバル変数または静的変数を初期化するとき、そのバーチャル関数がいずれかの派生クラスで noexcept(<false>) として指定されている場合、Polyspace はフラグを設定します。たとえば、基底クラスで noexcept(<true>) として宣言され、派生クラスでは noexcept(<false>) として宣言されたバーチャル初期化子関数を使用する場合、Polyspace はフラグを設定します。

  • 関数へのポインター:関数へのポインターを使用してグローバル変数または静的変数を初期化する場合、Polyspace は、関数へのポインターで例外が発生しないものと仮定します。

Polyspace は以下を無視します。

  • デストラクターで発生する例外

  • atext() 操作で発生する例外

Polyspace は、例外のチェックの際に動的なコンテキストも無視します。たとえば、特定の動的コンテキストでのみ例外を発生させる関数を使用してグローバル変数または静的変数を初期化するとします。Polyspace は、例外が決して発生しない場合でもそのような宣言にフラグを設定します。このような違反は、Polyspace でコメントを使用して正当化できます。

トラブルシューティング

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

すべて展開する

この例では、Polyspace が、例外を発生させる可能性のあるグローバル変数または静的変数の構築または初期化にフラグを設定する様子を示します。静的オブジェクトとグローバル オブジェクトがさまざまな呼び出し可能エンティティによって初期化される次のコードについて考えます。

#include <stdexcept>
#include <string>
class C
{
public:
	C ( ){throw ( 0 );}
	~C ( ){throw ( 0 );}
};
int LibraryFunc();                                    
int LibraryFunc_noexcept_false() noexcept(false);     
int LibraryFunc_noexcept_true() noexcept(true);       
int  g() noexcept {                                   
	throw std::runtime_error("dead code");
	return 0;
}
int f() noexcept {                                    
	return g();                                         
}
int init(int a) {
	if (a>10) {
		throw std::runtime_error("invalid case");
	}
	return a;
}
void* alloc(size_t s) noexcept {          
	return new int[s];
}
int a = LibraryFunc() + 
LibraryFunc_noexcept_true();            // Compliant
int c =
LibraryFunc_noexcept_false() +          // Noncompliant
LibraryFunc_noexcept_true();
static C static_c;                      //Noncompliant

C &get_static_c(){
	return static_c;
}
C global_c;                             //Noncompliant 
int a3 = f();                           //Compliant
int b3 = g();                           //Noncompliant
int a4 = init(5);                       //Noncompliant
int b5 = init(20);                      //Noncompliant
int* arr = (int*)alloc(5);              //Noncompliant

int main(){
	//...
}

  • グローバル ポインター arr は関数 alloc() を使用して初期化されます。alloc()new を使用してメモリを割り当てるため、プログラムの起動中の arr の初期化時に例外を発生させる可能性があります。Polyspace は、arr の宣言にフラグを設定し、関数 alloc() 内の new の使用を強調表示します。

  • 整数変数 b3 は、noexcept として指定された関数 g() を呼び出すことによって初期化されます。Polyspace は、g() の正しい例外指定は、throw() ステートメントが含まれているため、noexcept(false) であると推定します。g() を使用してグローバル変数 b3 を初期化すると、プログラムの起動中の arr の初期化中に例外が発生する可能性があります。Polyspace は、b3 の宣言にフラグを設定し、g() 内の throw ステートメントを強調表示します。f() を呼び出すことによる a3 の宣言にはフラグが設定されません。f() はスローしない関数 noexcept であり、別の関数 noexcept を呼び出すため、Polyspace は、f() が例外を発生させないと推定します。

  • グローバル変数の a4b5 は、関数 init() を呼び出すことによって初期化されます。関数 init() は、コンテキストに応じて、特定のケースで例外を発生させる可能性があります。Polyspace は関数の例外指定を静的に推定するため、init() はコンテキストに関係なく例外を発生させる可能性があると仮定します。その結果、Polyspace は、init()b5 の初期化時にしか例外を発生させないとしても、a4b5 の両方の宣言にフラグを設定します。

  • グローバル変数 global_int は、2 つの外部関数を呼び出すことによって初期化されます。外部関数 LibraryFunc_noexcept_false()noexcept(false) として指定され、Polyspace は、この外部関数が例外を発生させる可能性があると仮定します。Polyspace は、global_int の宣言にフラグを設定します。Polyspace は、a の宣言にはフラグを設定しません。これは、この宣言が noexcept(false) として指定されていない外部関数を呼び出すことによって初期化されるためです。

  • 静的変数 static_c と非静的グローバル変数 global_c は、クラス C のコンストラクターを使用することによって宣言および初期化されます。そのため、例外が発生する可能性があります。Polyspace は、これらの変数の宣言にフラグを設定し、クラス C のコンストラクター内の 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.