ERR03-C. 境界チェックインタフェースを呼び出す際は、実行時制約ハンドラを使用する
C言語仕様の付属書 K「Bounds-checking interfaces」が定義する関数の多くには、一連の実行時制約が仕様の一部として定められており、制約違反が発生した場合の処理を適切に行うことができる。ライブラリでは、プログラムの実行中、特定の関数で規定されている実行時制約への違反が発生しないことを検証する。実行時制約違反が発生すると、set_constraint_handler_s()
で登録された実行時制約ハンドラが呼び出される。
セクション 6.6.1には次のように記載されている [ISO/IEC TR 24731-1:2007]。
実行時制約ハンドラが呼び出される際、ハンドラには以下の引数がこの順番で渡される。
- 実行時制約違反を表す文字列へのポインタ。
- 空ポインタまたは処理系定義オブジェクトへのポインタ。
- 実行時制約ハンドラを呼び出した関数の返り値の型が
errno_t
として宣言されている場合は、その関数の返り値が渡される。それ以外の場合は、errno_t
型の正の値が渡される。処理系には、
set_constraint_handler_s()
関数の呼び出しが行われなかった場合や、set_constraint_handler_s()
へのハンドラ引数が空ポインタの場合に使われる、既定の制約ハンドラがある。既定のハンドラの動作は処理系定義であり、プログラムを終了または中断させる場合がある。
セクション6.1.4には次のように記載されている [ISO/IEC TR 24731-1:2007]。
実行時制約ハンドラは復帰してもよいし、しなくてもよい。ハンドラが復帰する場合、実行時制約に違反したライブラリ関数は、その関数の仕様の返り値のセクションに指定された、失敗を示す値を返すものとする。
これらの実行時制約ハンドラは、正常終了時とエラー発生時の値を同じ手段で通知するリスクを軽減する(「ERR02-C. 正常終了時の値とエラーの値は別の手段で通知する」を参照)。
違反コード (TR24731-1)
以下のコード例では、strcpy_s()
関数が呼び出されているが、実行時制約ハンドラが明示的に登録されていない。結果として、実行時制約違反の際は処理系定義の既定のハンドラが呼び出される。
errno_t function(char *dst1, size_t size){ char src1[100] = "hello"; if (strcpy_s(dst1, size, src1) != 0) { return -1; } /* ... */ return 0; }
規定のハンドラを使うことによって、処理系間で動作に一貫性がなくなり、プログラムは正常な終了ではなく強制終了される可能性がある。処理系定義の既定のハンドラは、特定の処理系に合わせた既定の処理を実行する。しかし、これは望ましい処理ではない場合がある。動作は処理系定義であり、すべての処理系に対して同じであることが保証されているわけではない。
ゆえに、実行時制約ハンドラを明示的に設定して、処理系間で一貫性のある動作を保証するのが賢明である。
適合コード(TR24731-1)
以下のコードでは、set_constraint_handler_s()
関数を呼び出すことによって明示的に実行時制約ハンドラを設定している。ハンドラの設定は通常、システムの初期化中に、この仕組みを使うあらゆる関数が呼び出される前に行われる。
constraint_handler_t handle_errors(void) { /* 実行時制約エラーの処理 */ } /*...*/ set_constraint_handler_s(handle_errors); /*...*/ /* 成功したら 0 を返す */ errno_t function(char *dst1, size_t size){ char src1[100] = "hello"; if (strcpy_s(dst1, size, src1) != 0) { return -1; } /* ... */ return 0; }
適合コード (Visual Studio 2008/.NET Framework 3.5)
ISO/IEC TR 24731-1関数はMicrosoftによって作成されたが、現在利用可能なバージョンのMicrosoft Visual Studioは、実行時制約ハンドラを設定する際にISO/IEC TR 24731-1で規定されているものと同じインタフェースを使用しない。Visual Studioではこれらの関数を「無効なパラメータハンドラ」と呼び、_set_invalid_parameter_handler()
関数の呼び出しによって設定する。ハンドラのプロトタイプもまた大きく異なる。
_invalid_parameter_handler handle_errors( const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved ) { /* 無効なパラメータの処理 */ } /*...*/ _set_invalid_parameter_handler(handle_errors) /*...*/ errno_t function(char *dst1, size_t size) { char src1[100] = "hello"; if (strcpy_s(dst1, size, src1) != 0) { return -1; } /* ... */ return 0; }
リスク評価
TR24731-1は、エラーが発生した場合に制約ハンドラが設定されていなければ既定のハンドラを実行すると述べている。既定のハンドラは処理系定義であり、「プログラムを終了または中断させる場合がある」。使用するすべての処理系における規定のハンドラの動作を把握し、その動作がアプリケーションに適さない場合はハンドラを明示的に設定せよ。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
ERR03-C |
低 |
低 |
中 |
P2 |
L3 |
関連するガイドライン
CERT C++ Secure Coding Standard | ERR03-CPP. Use runtime-constraint handlers when calling functions defined by TR24731-1 |
ISO/IEC TR 24731-1:2007 | Section 6.1.4, "Runtime-Constraint Violations" Section 6.6.1, "Runtime-Constraint Handling" |
参考資料
[MSDN] | "Parameter Validation" |
翻訳元
これは以下のページを翻訳したものです。
ERR03-C. Use runtime-constraint handlers when calling the bounds-checking interfaces (revision 43)