2014年7月17日木曜日

コンパイルや、ビルド時に付与したいオプション


コンパイルや、ビルド時にセキュアなオブジェクトコードを生成するために
利用すべきオプションを記録しておく。

g++ ビルド時のオプション
それぞれのオプションの意味はmanを参照してほしい。

$ g++ \
-g \
-fmudflap \
-lmudflap \
-fstack-protector \
-ftrapv \
-O2 \
-fno-strict-aliasing \
-Wall \
-Wextra \
-Wformat=2 \
-Wstrict-aliasing=2 \
-Wcast-qual \
-Wcast-align \
-Wwrite-strings \
-Wconversion \
-Wfloat-equal \
-Wpointer-arith \
-Wswitch-enum \
-Woverloaded-virtual \
-Weffc++ \
-o hoge.o \
hoge.cc


さらに、メモリの取得と解放のチェックを実施する環境変数を指定する。
$ export MALLOC_CHECK_=1

fmudflapオプションを付与して実行ファイルを作った場合、
mudflapライブラリを指定して実行する必要がある。
$ ./hoge.o -lmudflap

※gccとの差異
下はg++のみで有効。gccでは使えない。
-Woverloaded-virtual
-Weffc++



デバッグ時のみに使うべきオプション
デバッグ用のオプションなので、リリースビルド時には外すこと。

● -g
いわずもがな、デバッグオプション。
コンパイル、リンク時にDEBUG情報を付加して、
gdbなどのデバッガを使用するときに必要となる。


● -fmudflap
-gオプションと一緒に使う
また、リンク時にライブラリの指定が必要である。
-lmudflap

mudflapはコンパイル時に不正なポインタアクセスを検出できる。
仕組みとしてはアドレス周辺にチェックコードを埋め込むことで、
heap、stack、data、bss領域の変数の検査を行う。

mudflapが使えない場合はパッケージのインストールが必要である。
$ sudo yum install libmudflap libmudflap-devl

ポインタ周りのチェックにvalgrindを使うことも多いだろう。
mudflapとvalgrindの大きな違いは、
valgrindは解析対象の再コンパイルとリンクが不要で使える、
しかし、stack、data、bss変数の検査は不可(heapは可)、
また解析速度がmudflapに比べて遅い、
あたりだろう。
ついでなので、valgrindの使い方も後半に少しだけまとめておく。

同様の機能として-D_FORTIFY_SOURCEオプションを使ってもよい。
mudflapはデバッグ用の機能であったが、これはランタイムのオーバーヘッドが少ないため、
リソースビルドにも利用できる。
$ g++ -O2 --D_FORTIFY_SOURCE=1 ~
-O1以上の最適化が必須である。
コンパイル時にも、実行時にもオーバーフローチェックが走る。


● -fstack-protector
スタック上に確保されたchar変数あふれによって生じる、
リターンアドレスの書き変えを防止、検出する。

関数ポインタの指し先を上書けないように、
スタックレイアウトの調整、またcnary(カナリア)というガード値を
別途スタック上に挿入しそこへの改ざんの有無をチェックすることで実現する。

スタック破壊検出コードの生成はすべての関数に対しては行われないことに注意が必要である。
8バイト以上のchar配列が確保される関数だけが保護対象となる。
なお、この8バイトという閾値は、次のオプションを用いて変更できる。
--param ssp-buffer-size=N

どの関数についてもスタック破壊検出コードを生成させるには次のオプションを使う。
-fstack-protector-all


● -ftrapv
符号付き整数同士の加算・減算・乗算で整数のオーバーフローをランタイムに検出する。
検出した場合はabortする。
除算はNビット同士の除算結果がNビットを超えないためチェック対象外である。

オーバーフローの有無をチェックする関数とabort関数が
ハードコーディングされる。



valgrindの使い方
valgrindを使うにはビルド時に以下3点を気にかけておかなければならない。

・デバッグオプションを付与する
・最適化は最大O1まで
・スタティックリンクは避ける

スタティックリンクは避ける理由は
valgrindは仮想的なCPU上でプログラムを実行するのだが、
mallocなどの関数は置き換えが不可能であるためである。

% g++ -g -O0 -o hoge.o hoge.c

一般的によく使うオプションはこのあたりだろう。
$ valgrind \
--leak-check=full \
--leak-resolution=high \
--show-reachable=yes \
--trace-children=yes
./hoge.o 

先に一度書いているが、stack、data、bss変数の検査は不可なので注意すること。