090330

|


また嫌なメモリ破壊バグに出会った。なんとかそれを再現するコード。

このコードの主役は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にも、おかしいことを気付かせるルーチンを入れ ておきました。
これ、結構キツかった...。