次の方法で共有


SAL について

Microsoft ソース・コードのコメント言語 (SAL) では、パラメーターを使用する方法についてするために使用できる一連のコメント、それらについて行う前提と、いつ完了時に行うようになります。コメントは <sal.h>ヘッダー ファイルで定義されます。C++ の Visual Studio のコード分析は関数の分析を変更するには、SAL コメントを使用します。Windows ドライバー開発の SAL 2.0 に関する詳細については、" "を参照してください。SAL Windows ドライバーでは 2.0 のコメント

ネイティブ 15、B、および C++ は常に明確な目的と不変性に開発者に制限された方法のみです。SAL コメントを使用して、これらを実行しているスレッドを使用する方法を開発者がより的確に把握できるように、関数を詳しく記述できます。

SAL は、それを使用する理由必要があるか。

単純に指示すると、SAL は、コンパイラがこの方法でコードをチェックする低な方法です。

Hh916383.collapse_all(ja-jp,VS.110).gifSAL は、コードをより重要になります

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 バッファー サイズのコメントのコード分析ツールをバグをキャッチできますが含まれていました。

Hh916383.collapse_all(ja-jp,VS.110).gifSAL の基礎

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 コメントのコード例を示します。

Hh916383.collapse_all(ja-jp,VS.110).gif障害が検出 Visual Studio でコード分析ツールを使用する

例では、コード障害を検出するには、SAL コメントとともに Visual Studio でコード分析ツールが使用されます。次に、その方法を示します。

Visual Studio でコード分析ツールを使用すると、SAL

  1. Visual Studio では、SAL コメントを含む開きます。. C++ プロジェクト。

  2. メニュー バーで、[ビルド][ソリューションでコード分析を実行] を選択します。

    このセクションの_In_の例について考えます。そのコード分析を実行すると、この警告は表示:

    C6387 無効なパラメーター値 "パイント" は "0 "である可能性があります: これは、関数 "" InCallee の指定に従っていません。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 にすることはできません。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 値の関数検証チェックします。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 ポインターを渡すと、で返される前にバッファーが関数で初期化することを検証します。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 の場合、この関数の検証チェックします。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 以外であることを検証します。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 の場合、この関数の検証チェックします。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 ポインターを渡すこと、および戻る前にバッファーが関数で初期化することを検証します。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 値の場合、この関数の検証チェックします。

Hh916383.collapse_all(ja-jp,VS.110).gif例: _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 のベスト プラクティス

Hh916383.collapse_all(ja-jp,VS.110).gif既存のコードにコメントを追加できます。

SAL は、コードのセキュリティと信頼性を向上させることができる強力な手法です。SAL を学んだ後で、日常業務に新しいスキルを適用できます。新しいコードでは、全体で、SAL ベースの仕様を意図的に使用する; 古いコードでは、更新するたびに呼び出すため、コメントを追加し、インクリメンタル利点を向上させることができます。

Microsoft パブリック ヘッダーは既に注釈されます。したがって、は、プロジェクトの最初に最も多くの機能を利用するために Win32 API を呼び出すリーフ ノードの関数や関数に注釈を付けることを示します。

Hh916383.collapse_all(ja-jp,VS.110).gifMy は、注釈しますか。

ガイドラインを次に示します。:

  • すべてのポインター パラメーターを指定します。

  • コード分析でバッファーとポインターの安全性を確認できるように、値範囲のコメントを指定します。

  • 規則をロックおよびロックの副作用を指定します。詳細については、「ロック動作に注釈を付ける」を参照してください。

  • ドライバーのプロパティおよびそのほかのドメイン固有のプロパティを指定します。

またはコメントが実行されたことを確認するには、の意図をオフの中の、簡単にするために、すべてのパラメーターに注釈を付けることができます。

関連リソース

コード分析のチームのブログ

参照

関連項目

関数パラメーターおよび戻り値の注釈設定

関数の動作に注釈を付ける

構造体とクラスに注釈を付ける

ロック動作に注釈を付ける

注釈を適用するタイミングと場所の指定

ベスト プラクティスと例 (SAL)

その他の技術情報

SAL 注釈を使って C/C++ のコード障害を減らす方法