SAL について
Microsoft ソース・コードのコメント言語 (SAL) では、パラメーターを使用する方法についてするために使用できる一連のコメント、それらについて行う前提と、いつ完了時に行うようになります。コメントは <sal.h>ヘッダー ファイルで定義されます。C++ の Visual Studio のコード分析は関数の分析を変更するには、SAL コメントを使用します。Windows ドライバー開発の SAL 2.0 に関する詳細については、" "を参照してください。SAL Windows ドライバーでは 2.0 のコメント
ネイティブ 15、B、および C++ は常に明確な目的と不変性に開発者に制限された方法のみです。SAL コメントを使用して、これらを実行しているスレッドを使用する方法を開発者がより的確に把握できるように、関数を詳しく記述できます。
SAL は、それを使用する理由必要があるか。
単純に指示すると、SAL は、コンパイラがこの方法でコードをチェックする低な方法です。
SAL は、コードをより重要になります
SAL は、コードと設計を人間コード分析ツールでは、理解しやすくすることができます。C ランタイム関数 memcpyを示すこの例を使用する:
void * memcpy(
void *dest,
const void *src,
size_t count
);
この関数の動作を指定できますか。関数の実行、または呼び出されると、プログラムの正確性を保証するために、特定のプロパティは保持する必要があります。だけ例のような申告を確認することによって、何かわかりません。SAL コメントがないと、ドキュメントに依存するか、コメントをコーディングする必要があります。ここで memcpy の MSDN ドキュメントを示すことです:
"dest に src のコピーのバイト数。コピー元とコピー先が重複する場合 memcpy の動作は未定義です。重なり合う領域を処理するために memmove を使用します。セキュリティに関するメモ: はコピー先のバッファーはソース バッファーのサイズ以上であることを確認します。詳細については、" "を参照してくださいバッファー オーバーランを回避できます"。
ドキュメントは、プログラムの正確性を保証するためにそのコードを特定のプロパティを保持する必要があることを提案する情報の数が含まれています: bit
memcpy はソース バッファーからのコピー先のバッファーにバイト count をコピーします。
コピー先のバッファーはソース バッファー少なくとも大きさが必要です。
ただし、コンパイラは、ドキュメントまたは非公式のコメントを読み取ることができません。2 個のバッファーと count間にリレーションシップが存在する場合や関係を効果的に推測できないことを指定します。SAL は、次に示すように関数のプロパティと実装に関する詳細のわかりやすさを提供する:
void * memcpy(
_Out_writes_bytes_all_(count) void *dest,
_In_reads_bytes_(count) const void *src,
size_t count
);
これらのコメントが MSDN ドキュメントの情報は、より簡単です (似ていますが、パターンに従うことができるようにします。このコードを読み取るとき、バッファー オーバーランのセキュリティの問題を回避する方法を、すぐに、この関数のプロパティを理解します。、SAL は潜在的なバグの初期の検出の自動コード分析ツールの効率と効率を向上できる意味があるパターンにします。ユーザーが wmemcpyのこのおかしな実装を書き込むとする:
wchar_t * wmemcpy(
_Out_writes_all_(count) wchar_t *dest,
_In_reads_(count) const wchar_t *src,
size_t count)
{
size_t i;
for (i = 0; i <= count; i++) { // BUG: off-by-one error
dest[i] = src[i];
}
return dest;
}
この実装は 1 以外のエラーに共通が含まれます。さいわい、コード作成者は単独でこの関数を解析して、SAL バッファー サイズのコメントのコード分析ツールをバグをキャッチできますが含まれていました。
SAL の基礎
SAL は使用パターンで並べ替えて 4 種類の基本型のパラメーターを定義します。
カテゴリ |
パラメーターのコメント |
説明 |
---|---|---|
呼び出された関数への入力 |
_In_ |
データが呼び出された関数に渡して、読み取り専用として扱われます。 |
呼び出し元に呼び出された関数への入力および出力 |
_Inout_ |
使用できるデータは関数に渡され、場合によっては変更されます。 |
呼び出し元への出力 |
_Out_ |
呼び出し元/書き込みを呼び出された関数に対して空間について説明します。呼び出された関数は、領域にデータを書き込みます。 |
呼び出し元へのポインターの出力 |
_Outptr_ |
Output to callerのようにします。呼び出された関数によって返される値はポインターです。 |
この 4 種類の基本的なコメントは、さまざまな方法でも明示的に設定できます。既定では、パラメーターが、必須であると仮定されている注釈されたポインターが成功するために関数に null 以外である必要があります。基本的なコメントの最も一般的なバリエーションが null の場合、オプションのパラメーターであるが、関数ポインターがまだ処理をすることに成功することを示します。
このテーブルに必須パラメーターとオプションを区別する方法を示しています:
パラメーターは必須です |
パラメーターは省略できます |
|
---|---|---|
呼び出された関数への入力 |
_In_ |
_In_opt_ |
呼び出し元に呼び出された関数への入力および出力 |
_Inout_ |
_Inout_opt_ |
呼び出し元への出力 |
_Out_ |
_Out_opt_ |
呼び出し元へのポインターの出力 |
_Outptr_ |
_Outptr_opt_ |
これらのコメントは、正式で正確な方法の一つで初期化されていない値と無効な null ポインターの使用を特定するのに役立ちます。必須パラメーターに NULL を渡すと、クラッシュが起こる可能性またはが発生しました" 返されるエラー コードを "発生する可能性があります。いずれの場合も、関数はジョブを行うことに成功することはできません。
SAL の例
ここでは、基本的な SAL コメントのコード例を示します。
障害が検出 Visual Studio でコード分析ツールを使用する
例では、コード障害を検出するには、SAL コメントとともに Visual Studio でコード分析ツールが使用されます。次に、その方法を示します。
Visual Studio でコード分析ツールを使用すると、SAL
Visual Studio では、SAL コメントを含む開きます。. C++ プロジェクト。
メニュー バーで、[ビルド]、[ソリューションでコード分析を実行] を選択します。
このセクションの_In_の例について考えます。そのコード分析を実行すると、この警告は表示:
C6387 無効なパラメーター値 "パイント" は "0 "である可能性があります: これは、関数 "" InCallee の指定に従っていません。
例: _In_のコメント
_In_ のコメントは、コードを示しています:
パラメーターは有効である必要があり、変更されません。
関数は、単一要素のバッファーから読み取ります。
呼び出し元がバッファーを提供し、初期化する必要があります。
読み取り専用_In_ は、"" を指定します。よくある間違いは、_Inout_ のコメントが代わりに必要なパラメーターに _In_ を適用することです。
_In_ は許可されますが、ポインターのスカラーのアナライザーでは無視されます。
void InCallee(_In_ int *pInt)
{
int i = *pInt;
}
void GoodInCaller()
{
int *pInt = new int;
*pInt = 5;
InCallee(pInt);
delete pInt;
}
void BadInCaller()
{
int *pInt = NULL;
InCallee(pInt); // pInt should not be NULL
}
この例の Visual Studio コード分析を使用すると、呼び出し元が pIntの初期化バッファーに非 null ポインターを渡すことを検証します。この場合、pInt のポインターを null にすることはできません。
例: _In_opt_のコメント
_In_opt_ は _In_と同じです。ただし、入力パラメーターが null であり、関数はこれを確認する必要があります。
void GoodInOptCallee(_In_opt_ int *pInt)
{
if(pInt != NULL) {
int i = *pInt;
}
}
void BadInOptCallee(_In_opt_ int *pInt)
{
int i = *pInt; // Dereferencing NULL pointer ‘pInt’
}
void InOptCaller()
{
int *pInt = NULL;
GoodInOptCallee(pInt);
BadInOptCallee(pInt);
}
Visual Studio のコード分析はバッファーにアクセスする前に、NULL 値の関数検証チェックします。
例: _Out_のコメント
_Out_ は、一般的なシナリオを要素のバッファーを指す非 null ポインター サポートし、渡された関数が、要素を初期化します。呼び出し元は、呼び出しの前にバッファーを初期化する必要はありません; 呼び出された関数が返される前に、約束を初期化します。
void GoodOutCallee(_Out_ int *pInt)
{
*pInt = 5;
}
void BadOutCallee(_Out_ int *pInt)
{
// Did not initialize pInt buffer before returning!
}
void OutCaller()
{
int *pInt = new int;
GoodOutCallee(pInt);
BadOutCallee(pInt);
delete pInt;
}
Visual Studio でコード分析ツールは、呼び出し元が pInt のバッファーに非 null ポインターを渡すと、で返される前にバッファーが関数で初期化することを検証します。
例: _Out_opt_のコメント
_Out_opt_ は _Out_と同じです。ただし、パラメーターが null であり、関数はこれを確認する必要があります。
void GoodOutOptCallee(_Out_opt_ int *pInt)
{
if (pInt != NULL) {
*pInt = 5;
}
}
void BadOutOptCallee(_Out_opt_ int *pInt)
{
*pInt = 5; // Dereferencing NULL pointer ‘pInt’
}
void OutOptCaller()
{
int *pInt = NULL;
GoodOutOptCallee(pInt);
BadOutOptCallee(pInt);
}
Visual Studio のコード分析は pInt が逆参照される前に、返される前にバッファーが関数によって初期化 pInt が null 以外の場合、NULL の場合、この関数の検証チェックします。
例: _Inout_のコメント
_Inout_ が関数によって変更される可能性があるポインター パラメーターに注釈を付けるために使用されます。ポインターは呼び出しの前に有効な初期化データをポイントして変更しても、はの有効値を指定する必要があります。コメントは、関数が 1 要素のバッファリングとバッファーへの書き込みを行う場合があることを指定します。呼び出し元がバッファーを提供し、初期化する必要があります。
[!メモ]
_Out_のように、_Inout_、変更可能な値に適用する必要があります。
void InOutCallee(_Inout_ int *pInt)
{
int i = *pInt;
*pInt = 6;
}
void InOutCaller()
{
int *pInt = new int;
*pInt = 5;
InOutCallee(pInt);
delete pInt;
}
void BadInOutCaller()
{
int *pInt = NULL;
InOutCallee(pInt); // ‘pInt’ should not be NULL
}
Visual Studio でコード分析は、呼び出し元が pIntの初期化バッファーに非 null ポインターを渡すこと、バッファーを初期化することを、返される前に、pInt がまだ null 以外であることを検証します。
例: _Inout_opt_のコメント
_Inout_opt_ は _Inout_と同じです。ただし、入力パラメーターが null であり、関数はこれを確認する必要があります。
void GoodInOutOptCallee(_Inout_opt_ int *pInt)
{
if(pInt != NULL) {
int i = *pInt;
*pInt = 6;
}
}
void BadInOutOptCallee(_Inout_opt_ int *pInt)
{
int i = *pInt; // Dereferencing NULL pointer ‘pInt’
*pInt = 6;
}
void InOutOptCaller()
{
int *pInt = NULL;
GoodInOutOptCallee(pInt);
BadInOutOptCallee(pInt);
}
Visual Studio でコード分析が返される前にバッファーが関数で初期化する pInt が null であるバッファーにアクセスする前に、NULL の場合、この関数の検証チェックします。
例: _Outptr_のコメント
_Outptr_ がポインターを返す必要のあるパラメーターに注釈を付けるために使用されます。パラメーター自体には、の非 null ポインターと初期化データにその null ポインターの位置と呼び出された関数の戻り値にする必要があります。
void GoodOutPtrCallee(_Outptr_ int **pInt)
{
int *pInt2 = new int;
*pInt2 = 5;
*pInt = pInt2;
}
void BadOutPtrCallee(_Outptr_ int **pInt)
{
int *pInt2 = new int;
// Did not initialize pInt buffer before returning!
*pInt = pInt2;
}
void OutPtrCaller()
{
int *pInt = NULL;
GoodOutPtrCallee(&pInt);
BadOutPtrCallee(&pInt);
}
Visual Studio でコード分析は、呼び出し元が *pIntに非 null ポインターを渡すこと、および戻る前にバッファーが関数で初期化することを検証します。
例: _Outptr_opt_のコメント
_Outptr_opt_ は _Outptr_と同じです。ただし、パラメーターはパラメーターが NULL ポインターで省略可能な呼び出し元渡すことができます。
void GoodOutPtrOptCallee(_Outptr_opt_ int **pInt)
{
int *pInt2 = new int;
*pInt2 = 6;
if(pInt != NULL) {
*pInt = pInt2;
}
}
void BadOutPtrOptCallee(_Outptr_opt_ int **pInt)
{
int *pInt2 = new int;
*pInt2 = 6;
*pInt = pInt2; // Dereferencing NULL pointer ‘pInt’
}
void OutPtrOptCaller()
{
int **ppInt = NULL;
GoodOutPtrOptCallee(ppInt);
BadOutPtrOptCallee(ppInt);
}
Visual Studio でコード分析が返される前に *pInt が逆参照される前に、バッファーは、関数で初期化します。null 値の場合、この関数の検証チェックします。
例: _Out_のある_Success_のコメント
コメントは、ほとんどのオブジェクトに適用できます。特に、関数全体を付けます。関数の最も明白な特性の 1 つが、成功または失敗する可能性があることです。ただし、バッファー サイズと間の関連付けのように、C/C++ は、関数の成功または失敗を表現できません。_Success_ のコメントを使用して、関数の、成功のようになります。指定できます。_Success_ のコメントへのパラメーターは関数が成功したことが true の場合に示す式です。式は、コメント パーサーができる任意です。関数が成功した場合にのみ関数の戻り値が適用されるコメントの後の影響。正しいことを _Success_ するには _Out_ との対話方法を次の例に示します。戻り値を表すために return キーワードを使用できます。
_Success_(return != false) // Can also be stated as _Success_(return)
bool GetValue(_Out_ int *pInt, bool flag)
{
if(flag) {
*pInt = 5;
return true;
} else {
return false;
}
}
_Out_ のコメントにより、Visual Studio コード分析は、呼び出し元が pIntのバッファーに非 null ポインターを渡すこと、および戻る前にバッファーが関数で初期化することを検証します。
SAL のベスト プラクティス
既存のコードにコメントを追加できます。
SAL は、コードのセキュリティと信頼性を向上させることができる強力な手法です。SAL を学んだ後で、日常業務に新しいスキルを適用できます。新しいコードでは、全体で、SAL ベースの仕様を意図的に使用する; 古いコードでは、更新するたびに呼び出すため、コメントを追加し、インクリメンタル利点を向上させることができます。
Microsoft パブリック ヘッダーは既に注釈されます。したがって、は、プロジェクトの最初に最も多くの機能を利用するために Win32 API を呼び出すリーフ ノードの関数や関数に注釈を付けることを示します。
My は、注釈しますか。
ガイドラインを次に示します。:
すべてのポインター パラメーターを指定します。
コード分析でバッファーとポインターの安全性を確認できるように、値範囲のコメントを指定します。
規則をロックおよびロックの副作用を指定します。詳細については、「ロック動作に注釈を付ける」を参照してください。
ドライバーのプロパティおよびそのほかのドメイン固有のプロパティを指定します。
またはコメントが実行されたことを確認するには、の意図をオフの中の、簡単にするために、すべてのパラメーターに注釈を付けることができます。