前回
C/C++/ObjC メモリ破壊系バグのつぶし方 その1 - Pebble Coding
の続きです。
落ちる原因はいくつかパターンが決まっていますが、例を上げてみます。
ヒープアロケートしていない領域に書き込む
初期化していないポインタに対して、ヒープアロケートせずデータを書き込もうとしています。
#include <stdio.h> #include <string.h> typedef struct { int x1; char* x2; } Data; static int f(){ Data d; d.x1 = 1; memset(d.x2, 0, 32); return 1; } int main(int argc, char** argv){ f(); return 0; }
~$ ./a.out Segmentation fault: 11
ここではSegmentation faultで落ちてくれましたが、もっと複雑なソースの場合でリリースビルドで最適化が入ると、
関数の戻る位置がおかしくなり、 単純に処理がスキップされたりします。
これを防ぐには、構造体のメンバがアドレス変数いわゆるポインタの場合は全て0で初期化することです。
以下は、メモリ破壊系バグではなく動作未定義バグと言えますが、記載しておきます。
printfの引数不一致
#include <stdio.h> int main(int argc, char** argv){ printf("hoge", 1); }
このコードは大抵の場合、おかしな動作をすることもなく動きますが、
リリースビルドで最適化オプションがついたとき、おかしな動作をする時があります。
リリースビルドでしか再現しないバグなんて最悪です。
-Wallオプションを指定し、全てのコンパイラワーニングを有効にしてコンパイルし、
出てきたワーニングでやばいものを全て潰しましょう。
macOSの環境ではデフォルトでこのオプションが付いているようです。
~$ gcc a.c a.c:3:20: warning: data argument not used by format string [-Wformat-extra-args] printf("hoge", 1); ~~~~~~ ^ 1 warning generated. ~$ ./a.out hoge~$
-wオプションをつけてワーニングを無効にしてみるとメッセージが何も出ません。恐ろしいです。
~$ gcc a.c -w ~$
余談ですが、これから新規でプログラムを書く方は、
動作未定義などという狂った仕様を持つC/C++は使わずに、Rustやモダンな言語を使いましょう。
クリティカルな速度が要求されないならGCを持つ言語を使いましょう。
- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2011/12/23
- メディア: 単行本
- 購入: 14人 クリック: 98回
- この商品を含むブログ (25件) を見る
- 作者: Jim Blandy,Jason Orendorff,中田秀基
- 出版社/メーカー: オライリージャパン
- 発売日: 2018/08/10
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る