Linuxでできた組み込みシステムを新規のハードで
構築するする必要があったので、とりあえずソースコードを
コンパイルして起動したところ、異常終了で正常に動作
しませんでした。
作った本人は、「すでに製品として出荷されていてデバッグも
済んでいるので環境の問題だ」ということで手伝ってもらえず、
しぶしぶ自分でGDBを使って調査を開始しました。
(まぁ、手伝ってもらえないのはしょうがないんですよ。
予算もかかるし)
いろいろ調べた結果、mallocで確保したはずのポインタのアドレスが
あるタイミングでNULLになってしまうのが原因だったのですが、
「以前は動いていた」という先入観で苦戦してしまいました。
結局、「潜在的なバグがあって、コンパイラの環境が変わったことによって
表面化した」というのが結論でした。
抜粋すると以下のよなコードになるわけですが、
#include
#include
int main()
{
char str1[15];
char str2[21];
void* p;
p = malloc(100);
memset(
str1,
0x00,
21);
memset(
p,
0x00,
100);
free(p);
return 0;
}
この状態だと、重大なバグがあることが分かります。
str1へのmemsetでバッファ・オーバーランが発生していて、
15バイトの領域に21バイト分の0x00を書き込んでいます。
では、なぜ以前は動いていて今回は動かなかったのでしょうか?
これは、以前のコンパイラはローカル変数をスタックへ配置する時に
□□□str1□□□■■■■■str2■■■■■▲▲p▲▲
上記のような感じで配置していてstr1へのオーバーランの
影響がstr2に対して発生していたと思われます。
ところが、コンパイラのが変わったことによってstr2とpが
入れ替わってしまったと思われます。
□□□str1□□□▲▲p▲▲■■■■■str2■■■■■
コンパイラの最適化ロジックが変わると、変数の配置方法や
ロジックの展開方法など、ガラッと変わってしまうことは
珍しくありません。
こうなるとstr1へのオーバーランによりpがゼロクリアされる
ことになります。
こんな苦労をしないためにも、マジックナンバーの自粛や、
sizeofの活用は大切ですね。