また嫌なメモリ破壊バグに出会った。なんとかそれを再現するコード。
このコードの主役はfunca。引数をとり、その引数は使わなく、funcaがリター ンすることはないことがポイントだ。
gccのx86向けの最適化はすごくて、funcbがリターンすることがないことをわかっ ていてcallじゃなくjmpする。
これが問題なるコードを書いてしまったのだ。ブートストラップから自分自身を スレッドに移行する部分。
これ、結構キツかった...。
このコードの主役はfunca。引数をとり、その引数は使わなく、funcaがリター ンすることはないことがポイントだ。
void
funca (int a)
{
int i = 1234567890;
funcb (i);
}
void
funcb (int a)
{
while (1)
;
}
これを
i386-elf-gcc -O2 -S a.c
でコンパイルする。
gccのx86向けの最適化はすごくて、funcbがリターンすることがないことをわかっ ていてcallじゃなくjmpする。
funca: pushl %ebp movl %esp, %ebp movl $1234567890, 8(%ebp) ←引数を使いまわす。この関数用を次の関数用に。 leave jmp funcbそして戻ることはないのだから、引数領域のスタックをそのまま使いまわすの だ。ここまで最適化するとは思わなかったよ。
これが問題なるコードを書いてしまったのだ。ブートストラップから自分自身を スレッドに移行する部分。
__asm volatile ("movl %0, %%esp \n jmp *%1"::
"r"(current_thread->stack_bottom), "r"(board_main));
void
board_main (uint32_t arg)
{
while (/*CONSTCOND*/1)
;
}
このコードで上記のようなことになってしまい、スタックの底を割ってメモリ
を破壊したのだ...。
きっちりと
__asm volatile ("movl %2, %%esp\n"
"pushl %0\n"
"pushl %1\n"
"jmp *%3" ::
"g"(arg),
"g"(dummy_return),
"g"(current_thread->stack_bottom), "r"(board_main));
のように変更。うっかりnoreturnの関数が帰ろうとしてとんでもないEIPに飛ば
ないように、return addressにも、おかしいことを気付かせるルーチンを入れ
ておきました。
これ、結構キツかった...。
