/GS (バッファのセキュリティ チェック)
更新 : 2007 年 11 月
リターン アドレスを上書きするバッファ オーバーランを検出します。これはバッファ サイズに制限のないコードを攻略する一般的な方法です。バッファ オーバーランを検出するには、コンパイル済みコードにセキュリティ チェックを挿入します。
/GS[-]
解説
/GS は既定でオンになります。アプリケーションにセキュリティ上の脆弱性がないと想定される場合は、/GS- を使用します。
/GS の詳細については、「Compiler Security Checks In Depth」を参照してください。
コンパイラは、ローカルの文字列バッファを含む関数、x86 では例外処理を含む関数にチェックを挿入します。文字列バッファは、要素サイズが 1 バイトまたは 2 バイトの配列として定義されます。配列全体のサイズは少なくとも 5 バイト、または _alloca で割り当てられる任意のバッファになります。
すべてのプラットフォームで、関数にローカルの文字列バッファが含まれている場合、コンパイラは、関数のリターン アドレスを保護するために Cookie を挿入します。この Cookie は、関数の終了時、および 64 ビット オペレーティング システムではフレームのアンワインド時、x86 では何らかの形式の例外処理を含む関数でチェックされます。x86 では、コンパイラは、関数の例外ハンドラのアドレスを保護する際にも Cookie を挿入します。この Cookie は、フレームのアンワインド時にチェックされます。
/GS は、主にリターン アドレスへの直接バッファ オーバーランの検出を試行します。バッファ オーバーランは、関数呼び出しのリターン アドレスをスタックに格納する呼び出し規約のあるマシンでの方が簡単に攻略されます。たとえば、x86 は、関数呼び出しのリターン アドレスをスタックに格納する呼び出し規約を使用します。
バッファ オーバーランの問題を起こしやすいとコンパイラが判断した関数には、スタックのリターン アドレスの前に記憶領域が割り当てられます。関数の実行に入ったときに、割り当てられた記憶領域にセキュリティ Cookie が読み込まれます。Cookie の値は、モジュールの読み込み時に 1 回だけ計算されます。関数の実行の終了時に、ヘルパー関数が呼び出されて Cookie の値が変更されていないかどうかを確認します。
値が同じでない場合は、スタックの上書きが行われている可能性があるため、処理は終了します。Visual C++ 2005 より前のバージョンでは、スタックの上書きを報告するダイアログ ボックスが表示されていました。
/GS はまた、脆弱なパラメータが関数に渡されないように保護します。脆弱なパラメータとは、ポインタ、C++ 参照、または内部にポインタ、文字列バッファ、または C++ 参照を含む C 構造体 (C++ POD 型) です。
脆弱なパラメータは、Cookie およびローカル変数より前に割り当てられます。バッファ オーバーランによって、これらのパラメータが上書きされることがあります。これらのパラメータを使用する関数のコードでは、関数が戻る前に攻撃を受け、セキュリティ チェックが回避される可能性があります。この危険を最小化するために、コンパイラは、関数プロローグで、脆弱なパラメータのコピーを作成し、任意のバッファのストレージ領域の下に配置します。
コンパイラは、次の場合は脆弱なパラメータに対するセキュリティの保護を行いません。
バッファを含まない関数の場合。
最適化 (/O オプション (コードの最適化)) が無効の場合。
可変個引数リスト (...) を取る関数の場合。
naked (C++) でマークされた関数の場合。
最初のステートメントにインライン アセンブラ コードを含む関数の場合。
パラメータが、バッファ オーバーランが発生したときに攻略される可能性が低い方法でのみ使用されている場合。
/GS では、セキュリティ Cookie の初期化が必要です。この Cookie は、Cookie を使用する関数を実行する前に初期化する必要があります。セキュリティ Cookie は、EXE または DLL の開始時に初期化する必要があります。この初期化は、既定の CRT エントリ ポイント (mainCRTStartup、wmainCRTStartup、WinMainCRTStartup、wWinMainCRTStartup、または _DllMainCRTStartup) を使用すると自動的に実行されますが、別のエントリ ポイントを使用する場合は、__security_init_cookie を呼び出して手動で実行する必要があります。
/GS は、/clr (共通言語ランタイムのコンパイル) を指定してコンパイルする場合、マネージ関数でサポートされます。
/GS は、すべてのバッファ オーバーラン攻撃からセキュリティを保護できるわけではありません。たとえば、バッファと vtable が同じオブジェクトにある場合、バッファ オーバーランによって vtable が破損し、攻撃の発生を許す可能性があります。
/GS を使用する場合でも、できる限り安全なコードを記述する必要があります。そのためには、バッファ オーバーランがないことを確認します。/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);
}