/GS (バッファーのセキュリティ チェック)
関数のリターン アドレス、例外ハンドラーのアドレス、または特定の型のパラメーターを上書きするバッファー オーバーランを検出します。ハッカーは、バッファー オーバーランを発生させるという方法を使用して、バッファー サイズ制限を強制しないコードを攻撃します。
/GS[-]
解説
/GS は既定でオンになります。アプリケーションにセキュリティ上の脆弱性がないと想定される場合は、/GS- を使用します。/GS の詳細については、「コンパイラ セキュリティの徹底調査」を参照してください。バッファー オーバーランの抑制の詳細については、「safebuffers」を参照してください。
セキュリティ チェック
バッファー オーバーランの問題を起こしやすいとコンパイラが判断した関数には、スタックのリターン アドレスの前に記憶領域が割り当てられます。関数の実行開始時に、割り当てられた記憶領域にセキュリティ クッキー が読み込まれます。クッキーの値は、モジュールの読み込み時に 1 回だけ計算されます。関数の実行の終了時に、および 64 ビット オペレーティング システムでのフレームのアンワインド時に、ヘルパー関数が呼び出されてクッキーの値が変更されていないかどうかが確認されます。異なる値は、スタックの上書きが発生した可能性があることを示します。異なる値が検出された場合、プロセスは終了します。
GS バッファー
バッファー オーバーランのセキュリティ チェックは、GS バッファーで実行されます。GS バッファーは、次のいずれかを使用できます。
4 バイトを超える配列で、3 つ以上の要素を持ち、要素型がポインター型ではない配列。
サイズが 8 バイトを超え、ポインターを含まないデータ構造体。
_alloca 関数を使用して割り当てられたバッファー。
GS バッファーを含むクラスまたは構造体。
たとえば、次のステートメントで GS バッファーを宣言します。
char buffer[20];
int buffer[20];
struct { int a; int b; int c; int d; } myStruct;
struct { int a; char buf[20]; };
ただし、次のステートメントでは GS バッファーは宣言されません。最初の 2 つの宣言には、ポインター型の要素が含まれます。3 番目と 4 番目のステートメントでは、サイズが非常に小さい配列を宣言しています。5 番目のステートメントでは、x86 プラットフォームでサイズが 8 バイトを超えない構造体を宣言しています。
char *pBuf[20];
void *pv[20];
char buf[4];
int buf[2];
struct { int a; int b; };
セキュリティ クッキーの初期化
/GS コンパイラ オプションでは、クッキーを使用する関数を実行する前に、セキュリティ クッキーを初期化する必要があります。セキュリティ クッキーは、EXE または DLL の開始時に初期化する必要があります。この初期化は、既定の CRT エントリ ポイント (mainCRTStartup、wmainCRTStartup、WinMainCRTStartup、wWinMainCRTStartup、または _DllMainCRTStartup) を使用すると、自動的に実行されます。別のエントリ ポイントを使用する場合は、__security_init_cookie を呼び出して、セキュリティ クッキーを手動で初期化する必要があります。
保護される対象
/GS コンパイラ オプションにより、次の項目が保護されます。
関数呼び出しの戻りアドレス。
関数の例外ハンドラーのアドレス。
Vulnerable 関数パラメーター。
すべてのプラットフォームで、/GS は、リターン アドレスへのバッファー オーバーランの検出を試行します。バッファー オーバーランは、関数呼び出しのリターン アドレスをスタックに格納する呼び出し規約を使用する、x86、x64 などのプラットフォームでの方が簡単に攻撃されます。
x86 で、関数が例外ハンドラーを使用する場合、コンパイラはセキュリティ クッキーを挿入して、例外ハンドラーのアドレスを保護します。このクッキーは、フレームのアンワインド時にチェックされます。
/GS は、関数に渡される脆弱なパラメーターを保護します。脆弱なパラメーターとは、ポインター、C++ 参照、内部にポインターを含む C 構造体 (C++ POD 型)、または GS バッファーです。
脆弱なパラメーターは、クッキーおよびローカル変数より前に割り当てられます。バッファー オーバーランによって、これらのパラメーターが上書きされることがあります。また、これらのパラメーターを使用する関数のコードでは、関数から制御が戻る前、およびセキュリティ チェックが実行される前に、攻撃を受ける可能性があります。この危険を最小化するために、コンパイラは、関数プロローグで、脆弱なパラメーターのコピーを作成し、任意のバッファーのストレージ領域の下に配置します。
コンパイラは、次の場合は脆弱なパラメーターのコピーを作成しません。
GS バッファーを含まない関数の場合。
最適化 (/O オプション) が無効の場合。
可変個引数リスト (...) を持つ関数の場合。
naked でマークされた関数の場合。
最初のステートメントにインライン アセンブラー コードを含む関数の場合。
パラメーターが、バッファー オーバーランが発生したときに攻撃される可能性が低い方法でのみ使用されている場合。
保護されない対象
/GS コンパイラ オプションは、すべてのバッファー オーバーラン攻撃からセキュリティを保護できるわけではありません。たとえば、バッファーと vtable が同じオブジェクトにある場合、バッファー オーバーランによって vtable が破損する可能性があります。
/GS を使用する場合でも、バッファー オーバーランの発生しない安全なコードを常に記述する必要があります。
このコンパイラ オプションを Visual Studio で使用するには
ソリューション エクスプローラーで、プロジェクトを右クリックし、[プロパティ] をクリックします。
詳細については、「方法 : プロジェクト プロパティ ページを開く」を参照してください。
[プロパティ ページ] ダイアログ ボックスで、[C/C++] フォルダーをクリックします。
[コード生成] プロパティ ページをクリックします。
[バッファー セキュリティ チェック] プロパティを変更します。
このコンパイラ オプションをコードから設定するには
- 「BufferSecurityCheck」を参照してください。
使用例
この例では、バッファー オーバーランが発生します。このため、実行時にアプリケーションで障害が発生します。
// compile with: /c /W1
#include <cstring>
#include <stdlib.h>
#pragma warning(disable : 4996) // for strcpy use
// Vulnerable function
void vulnerable(const char *str) {
char buffer[10];
strcpy(buffer, str); // overrun buffer !!!
// use a secure CRT function to help prevent buffer overruns
// truncate string to fit a 10 byte buffer
// strncpy_s(buffer, _countof(buffer), str, _TRUNCATE);
}
int main() {
// declare buffer that is bigger than expected
char large_buffer[] = "This string is longer than 10 characters!!";
vulnerable(large_buffer);
}