例外コード0xC0000409
この例外コードについて
この例外コードはSTATUS_STACK_BUFFER_OVERRUNのエラー コードで、一般的にはシステムやランタイムがスタック領域におけるバッファーオーバーランの検出を示しています。Visual C++アプリケーションでは、バッファーオーバーランに限らず実行が継続できないエラーをランタイムが検出した場合に様々な状況でこのエラーコードが記録される可能性があります。
以下は例外コード0xc0000409が発生する C++コードの例です。
#include <string.h>
int main()
{
strcpy_s(NULL, 0, NULL);
}
以下はVisual Studio 2015以降でビルドした場合の例ですが、Visual Studioのバージョンやランタイムのリンク方法によってモジュール名が異なる可能性があります。
障害が発生しているアプリケーション名: ConsoleApplication1.exe、バージョン: 0.0.0.0、タイム スタンプ: 0x5fa16ece
障害が発生しているモジュール名: ucrtbase.dll、バージョン: 10.0.19041.546、タイム スタンプ: 0x73123758
例外コード: 0xc0000409
障害オフセット: 0x0009d112
障害が発生しているプロセス ID: 0x424c
障害が発生しているアプリケーションの開始時刻: 0x01d6b1f11424c381
障害が発生しているアプリケーション パス: C:\Users\user1\source\repos\ConsoleApplication1\Release\ConsoleApplication1.exe
障害が発生しているモジュール パス: C:\WINDOWS\System32\ucrtbase.dll
例外コードが0xc0000409となることはVisual C++の想定された動作で、ランタイムが実行を継続できないエラーを検知した場合は__failfast 関数が呼び出されることに起因しています。__fastfail関数にはさらにエラーの種類を示すサブコード5(FAST_FAIL_INVALID_ARG)が指定されますが、この値はApplication Errorのログには出力されず、後続のWindows Error Reportingのイベントログで以下のようにP9の値として出力されます。
問題の署名:
P1: ConsoleApplication22.exe
P2: 0.0.0.0
P3: 5fa16ece
P4: ucrtbase.dll
P5: 10.0.19041.546
P6: 73123758
P7: 0009d112
P8: c0000409
P9: 00000005
P10:
サブ コードには以下をはじめとする様々な種類があり、Windows SDKのwinnt.hで定義されています。
#define FAST_FAIL_LEGACY_GS_VIOLATION 0
#define FAST_FAIL_VTGUARD_CHECK_FAILURE 1
#define FAST_FAIL_STACK_COOKIE_CHECK_FAILURE 2
#define FAST_FAIL_CORRUPT_LIST_ENTRY 3
#define FAST_FAIL_INCORRECT_STACK 4
#define FAST_FAIL_INVALID_ARG 5
#define FAST_FAIL_GS_COOKIE_INIT 6
#define FAST_FAIL_FATAL_APP_EXIT 7
...
winnt.hは、Windows 10 SDKの場合C:\Program Files (x86)\Windows Kits\10\Include\10.x.x.x\um\winnt.hにインストールされています。上記以外にも多くのサブコードが定義されているため、興味のある方は参照してください。
よく見られるサブコード
例外コード0xC0000409に関して、よく見られるサブコードには以下のようなものがあります。
FAST_FAIL_STACK_COOKIE_CHECK_FAILURE (2)
このサブコードは、バッファーオーバーランなどの理由でスタックの整合性が損なわれた場合に言発生します。例えば、以下のコード例では、スタックに確保したバッファーに対してGetModuleFileNameA関数でサイズを超える書き込みを発生させています。/GSオプションが有効な場合、Visual C++ランタイムは関数の開始と終了でスタックの整合性を検証し、破損が検出された場合は__fastfail(FAST_FAIL_STACK_COOKIE_CHECK_FAILURE)を呼び出してプロセスを強制終了します。
// /GS : [構成プロパティ] - [C/C++] - [コード生成] - [セキュリティチェック] - [セキュリティチェックを有効にします]
#include <windows.h>
int main()
{
char str[8];
GetModuleFileNameA(NULL, str, 16);
}
クラッシュダンプファイルを取得してコールスタックを確認すると、__report_gsfailure関数が呼び出されていることからスタックの整合性が損なわれたことが判断できます。
0:000> k
# ChildEBP RetAddr
00 0078fc60 00fb102a ConsoleApplication2!__report_gsfailure+0x18
01 0078fc74 00fb11fe ConsoleApplication2!main+0x2a
02 (Inline) -------- ConsoleApplication2!invoke_main+0x1c
03 0078fcbc 774c5d49 ConsoleApplication2!__scrt_common_main_seh+0xfd
04 0078fccc 7788d2fb kernel32!BaseThreadInitThunk+0x19
05 0078fd24 7788d281 ntdll!__RtlUserThreadStart+0x2b
06 0078fd34 00000000 ntdll!_RtlUserThreadStart+0x1b
なお、__report_gsfailure関数の実装はC:\Program Files\Microsoft Visual Studio<vs_version>\Enterprise\VC\Tools\MSVC<vc_version>\crt\src\vcruntime\gs_report.cから確認できます。
FAST_FAIL_INVALID_ARG (5)
このサブコードは、無効なパラメーターが関数に渡された場合に発生します。例えば、以下のコード例ではstrcpy_s関数にNULLポインターが渡されています。strcpy_s関数は無効なパラメーターを検出した場合、無効なパラメーターハンドラーを呼び出します。無効なパラメーターハンドラーが登録されていない場合、ランタイムは__fastfail(FAST_FAIL_INVALID_ARG)を呼び出してプロセスを強制終了します。
#include <string.h>
int main()
{
strcpy_s(NULL, 0, NULL);
}
この場合、クラッシュダンプファイルを取得してデバッガーでコールスタックを確認すると、_invalid_parameter関数が呼び出されていることから無効なパラメーターが検出されたことが判断できます。
0:000> k
# ChildEBP RetAddr
00 003af7f4 762f3b21 ucrtbase!_invoke_watson+0x12
01 003af820 7634b2bc ucrtbase!_invalid_parameter_internal+0x7b
02 003af864 762fdfb0 ucrtbase!_invalid_parameter+0x50
03 003af878 76323a01 ucrtbase!_invalid_parameter_noinfo+0x10
04 003af88c 00d3100c ucrtbase!strcpy_s+0x51
05 003af89c 00d311e2 ConsoleApplication2!main+0xc
06 (Inline) -------- ConsoleApplication2!invoke_main+0x1c
07 003af8e4 774c5d49 ConsoleApplication2!__scrt_common_main_seh+0xfd
08 003af8f4 7788d2fb kernel32!BaseThreadInitThunk+0x19
09 003af94c 7788d281 ntdll!__RtlUserThreadStart+0x2b
0a 003af95c 00000000 ntdll!_RtlUserThreadStart+0x1b
FAST_FAIL_FATAL_APP_EXIT (7)
このサブコードは、アプリケーションやライブラリ内でabort関数が明示的に呼び出された場合に発生します。abort関数を呼び出した場合、ランタイムは__fastfail(FAST_FAIL_FATAL_APP_EXIT)を呼び出してプロセスを強制終了します。
#include <process.h>
int main()
{
abort();
}
0:000> k
# ChildEBP RetAddr
00 00fefae8 00e611d6 ucrtbase!abort+0x31
01 (Inline) -------- ConsoleApplication2!invoke_main+0x1c
02 00fefb30 774c5d49 ConsoleApplication2!__scrt_common_main_seh+0xfd
03 00fefb40 7788d2fb kernel32!BaseThreadInitThunk+0x19
04 00fefb98 7788d281 ntdll!__RtlUserThreadStart+0x2b
05 00fefba8 00000000 ntdll!_RtlUserThreadStart+0x1b
abort関数の実装はC:\Program Files (x86)\Windows Kits\10\Source<version>\ucrt\startup\abort.cppから確認できます。
その他のサブコード
上記以外のサブコードは私が知る限りユーザーモードアプリケーションで見かけることはありませんが、公式ドキュメントの内容を元に概要を記載しておきます。
FAST_FAIL_LEGACY_GS_VIOLATION (0)
このコードはwinnt.hに記載されているとおり過去の互換性のために予約されており、実際には使用されません。
// N.B. Failure Code zero (0) should not be used
// It is reserved for compatibility with previous handling of the STATUS_STACK_BUFFER_OVERRUN exception status code
FAST_FAIL_VTGUARD_CHECK_FAILURE (1)
VTGuard インストルメンテーション コードで、不正な仮想関数テーブルを使用する試みが検出されました。 通常、C++ オブジェクトが破損し、破損したオブジェクトの this ポインターを使用して仮想メソッド呼び出しが試行されました。
FAST_FAIL_CORRUPT_LIST_ENTRY (3)
LIST_ENTRYが破損しました (二重削除など)。
FAST_FAIL_INCORRECT_STACK (4)
予約済み。
FAST_FAIL_GS_COOKIE_INIT (6)
スタック Cookie のセキュリティ Cookie がローダーによって正しく初期化されませんでした。 これは、Windows 8 でのみ実行するようにドライバーをビルドし、以前のバージョンの Windows でドライバー イメージを読み込もうとしたことが原因である可能性があります。 この問題を回避するには、以前のバージョンの Windows で実行するドライバーをビルドする必要があります。