Pebble Coding

ソフトウェアエンジニアによるIT技術、数学の備忘録

C/C++/ObjC メモリ破壊系バグのつぶし方 その2

前回
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を持つ言語を使いましょう。

詳解 Objective-C 2.0 第3版

詳解 Objective-C 2.0 第3版

プログラミングRust

プログラミングRust

  • 作者: Jim Blandy,Jason Orendorff,中田秀基
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2018/08/10
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る