コアダンプの数だけ強くなれるよ

見習いエンジニアの備忘log

C/C++のメモリ破壊を3種の方法で検出してみる

C/C++のプログラマーなら一度は悩ませられるであろうメモリ関連のバグ。その内の1つであるメモリ破壊は作りこむのは簡単ですが、後々見つける事が難しくなる場合も多くやっかいな奴です。

今回はそんなメモリ破壊検出の助けとなるツールをいくつか試してみます。

メモリ破壊を起こすソースコード


まず、メモリ破壊を起こすサンプルとしてバッファオーバーフローを起こすoverflow.cを準備します。

overflow.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void)
{
    uint8_t *ptr = NULL;

    // 64byte分のメモリ領域を取得
    ptr = (uint8_t *)malloc(64);
    if (NULL == ptr) {
        fprintf(stderr, "out-of-memory\n");
        exit(EXIT_FAILURE);
    }

    // メモリ破壊箇所
    // メモリ領域を1byteはみ出した位置に適当なデータを挿入
    ptr[64] = 0x5a;
    free(ptr);

    return 0;
}



Electric Fenceによる検出


Electric Fenceはmalloc()関数を使用する際にプログラマーがやりがちなバグの検出を手助けするライブラリーです。

通常、malloc()で取得したメモリ領域を超えて不正アクセスを行っても、そこがアクセス可能な領域である場合はプログラムは起動し続けてしまいバグに気づかないことがあります。

Electric Fenceを使用するとそういったケースでも不正メモリアクセスを検出し即時にコアダンプするようになります。

ライブラリーのインストールとコンパイル時に-lefenceを指定することで使用できます。


# ElectricFenceのインストール
[user@localhost ef]$ sudo yum -y install ElectricFence


# コアダンプ時にコアファイルを出力するように設定
[user@localhost ef]$ ulimit -c unlimited


# プログラムのコンパイル
[user@localhost ef]$ gcc -g -O0 overflow.c -lefence


# プログラムの実行
[user@localhost ef]$ ./a.out

  Electric Fence 2.2.2 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com> 
Segmentation fault (core dumped)


# gdbによる解析
[user@localhost ef]$ gdb a.out core.2396
...(途中略)
[New LWP 2396]
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000000000400802 in main () at overflow.c:17
17          ptr[64] = 0x5a; ★ここでコアダンプ
Missing separate debuginfos, use: debuginfo-install ElectricFence-2.2.2-39.el7.x86_64 glibc-2.17-157.el7_3.5.x86_64
(gdb)



Valgrindによる検出


Varlgrindはメモリリーク検出やメモリデバッグを行うことができるツールです。解析の速度は低速ですがデバッグ対象のプログラムを再コンパイルしなくても使えるというメリットがあります。


# Valgrindのインストール
[user@localhost ef]$ sudo yum -y install valgrind


# プログラムのコンパイル
[user@localhost ef]$ gcc -g -O0 overflow.c


# プログラムの実行
[user@localhost ef]$ valgrind --leak-check=full ./a.out
==2430== Memcheck, a memory error detector
==2430== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==2430== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==2430== Command: ./a.out
==2430==
==2430== Invalid write of size 1 ★ここで検出
==2430==    at 0x4006A2: main (overflow.c:17)
==2430==  Address 0x51f6080 is 0 bytes after a block of size 64 alloc'd
==2430==    at 0x4C28BE3: malloc (vg_replace_malloc.c:299)
==2430==    by 0x400666: main (overflow.c:10)
==2430==
==2430==
==2430== HEAP SUMMARY:
==2430==     in use at exit: 0 bytes in 0 blocks
==2430==   total heap usage: 1 allocs, 1 frees, 64 bytes allocated
==2430==
==2430== All heap blocks were freed -- no leaks are possible
==2430==
==2430== For counts of detected and suppressed errors, rerun with: -v
==2430== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)



Mudflapによる検出


MudflapはGCC4に実装されているデバッグ機能です。バッファオーバーフローやメモリリークをプログラム実行時に検出することが可能です。

ライブラリーのインストールとコンパイル時に-fmudflap -lmudflapを指定することで使用できます。


# ライブラリーのインストール
[user@localhost ef]$ sudo yum -y install  libmudflap libmudflap-devel


# プログラムのコンパイル
[user@localhost ef]$ gcc -g -O0 -fmudflap -lmudflap overflow.c


# プログラムの実行
[user@localhost ef]$ ./a.out
*******
mudflap violation 1 (check/write): time=1508643754.888009 ptr=0x1864e10 size=1
pc=0x7f475e654308 location=`overflow.c:17:13 (main)'
      /lib64/libmudflap.so.0(__mf_check+0x18) [0x7f475e654308]
      ./a.out(main+0xb6) [0x400bb3]
      /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f475e2abb35]
Nearby object 1: checked region begins 1B after and ends 1B after
mudflap object 0x1864e70: name=`malloc region'
bounds=[0x1864dd0,0x1864e0f] size=64 area=heap check=0r/0w liveness=0
alloc time=1508643754.887851 pc=0x7f475e654718
      /lib64/libmudflap.so.0(__mf_register+0x18) [0x7f475e654718]
      /lib64/libmudflap.so.0(__real_malloc+0xbf) [0x7f475e65523f]
      ./a.out(main+0x2e) [0x400b2b]
      /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f475e2abb35]
number of nearby objects: 1



まとめ


各ツールの比較

ツール名 デバッグ対象の
再コンパイル
実行速度 動的メモリの
チェック機能
静的メモリの
チェック機能
分かりやすさ
Electric Fence 必要 ×
Valgrind 不要
Mudflap 必要


各ツールの使用感を比較するとこんな感じでしょうか。 どれも一長一短あるので状況に応じて使い分けるのが良さそうです。