小さい頃はエラ呼吸

いつのまにやら肺で呼吸をしています。


gflags.exeとでヒープメモリ関連のバグを検出する

はじめに

C言語でmallocした領域をヒープメモリと呼びます。
ヒープメモリを解放しなかったり、獲得した領域をオーバして書き込んだりすると、検出するのが難しいバグにつながります。
こうしたヒープメモリ関連のバグを検出するツールがgflags.exeになります。

ひなた先生が教えるデバッグが256倍速くなるテクニック (Software Design Books)
やねうらお
技術評論社
売り上げランキング: 371,603

gflagsの入手

gflagsは、Windows 8.1 用 Windows ソフトウェア開発キット (Windows SDK) に含まれています。
以下のページからダウンロードして、インストールします。

gflagsの有効/無効

コマンドプロンプトを起動し、gflagsのインストールフォルダへ移動します。*1

cd C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64

gflagsで監視したいアプリケーションを指定して、gflagsを有効にします。

gflags.exe /p /enable sampleApp01.exe /full

検証が終わったら、以下のコマンドでgflagsを無効にします。

gflags.exe /p /disable sampleApp01.exe

以下のコマンドで現在gflagsが有効になっているアプリケーションを調べることができます。

gflags.exe /p
>  sampleapp01.exe: page heap enabled with flags (full traces )
サンプルアプリケーションの作成

ヒープメモリの扱いに問題のあるアプリケーションを作成してみます。
サンプルアプリケーションでは、8byteのヒープメモリを確保し、そこに16byteのデータを書き込み、バッファオーバを発生させます。

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>

int _tmain(int argc, _TCHAR* argv[])
{
  printf("%s start\n", __FUNCTION__);
  char *p;
  p = (char*)malloc(8);
  ZeroMemory(p, 16);  //->ここでバッファオーバ

  free(p);
  printf("%s end\n", __FUNCTION__);
  _getch();
  return 0;
}

このプログラム、リリース版で普通に動かしてみると正常に動くときもあれば、異常終了することもあります。
これをgflagsの監視対象にすると、確実にアプリケーションをクラッシュさせることができます。

アプリケーションのクラッシュ原因を探る

アプリケーションのクラッシュ原因を突き止めるには、クラッシュダンプを採取すると良いです。
コマンドプロンプトから以下のコマンドを実行すると、アプリケーションの異常終了時に、クラッシュダンプを出力するようになります。

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpFolder /t REG_EXPAND_SZ /d ^%LOCALAPPDATA^%\CrashDumps /f
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpCount /t REG_DWORD /d 10 /f
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpType /t REG_DWORD /d 2 /f

クラッシュダンプは以下のフォルダに出力されます。

  • C:\Users\Administrator\AppData\Local\CrashDumps

クラッシュダンプが採取できたら、WinDbgで解析を行います。
スタートメニューからWindows Kits→WinDbgを起動します。

WinDbgにさきほど採取したクラッシュダンプをドラッグ&ドロップします。

クラッシュダンプを読み込ますと、シンボルが読み込めないという旨のエラーがでていることが分かります。そこで、シンボルファイルを読み込ませます。
メニューからFile→Symbol File Pathを選択します。
シンボルパスに、以下を指定するとMicrosoftがWebに公開しているシンボルファイルをダウンロードすることができます。

SRV*c:\symbols*http://msdl.microsoft.com/download/symbols


シンボルの読み込みが完了したら、画面下部のテキストエリアに以下のコマンドを入力してEnterキー押すと、ダンプファイルの解析がはじまります。

!analyze -v

解析結果からZeroMemory(p, 16);の部分がクラッシュの原因ということが分かります。ダンプファイルから、ソースコードの行まで特定できるのがすごいですね。

おわりに

以上がgflags.exeを使ったヒープ破壊の解析方法です。
!analyze -vの結果、思うような解析が得られない場合は、サンプルアプリケーションのビルド時に生成された.pdbファイルをシンボルとして読み込ませると多くの情報が得られます。

*1:Windows 2008 R2の場合のフォルダパスで説明しています。