2009年3月5日木曜日

(FreeBSD) apacheのhttpd-error.logに、[notice] child pid 数字 exit signal Illegal instruction (4) ~ 原因がわかりにくかった…

apacheのhttpd-error.logに、



[Wed Mar 04 12:29:29 2009] [notice] child pid 64027 exit signal Illegal instruction (4)
[Wed Mar 04 12:29:29 2009] [notice] child pid 64026 exit signal Illegal instruction (4)



なんていう気になるメッセージが、ポコポコ増えていく。



portupgradeしたとき、何かの依存関係が正しくなくて、あれこれ再ビルドが必要なのか?!なんてことを思ったんですが、違いました。



ぜんぜん違いました。



原因は、PHPスクリプトのバグ。関数呼び出しが、無限ループしてました。



本当はもっと複雑なスクリプトだったんですが、単純化しちゃうと、たとえば、こんな感じのことが書いてありました。



% cat loop.php
<?php
function infiniteLoop()
{
    infiniteLoop();
}



infiniteLoop();



?>



これを実行すると



% php loop.php
Segmentation fault



はあ、そうですか。



もうちょっとわかりやすいエラーメッセージが出てくれればよかったのに・・・





不親切で、すべては自己責任なプログラミング言語Cではどうでしょうか。



% cat loop.c
void loop() { loop(); }



int main( int c, char** v )
{
  loop();
}
% gcc -g loop.c
% ./a.out
Segmentation fault



% gdb a.out
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd"...
(gdb) run
Starting program: /home/nhh/a.out



Program received signal SIGSEGV, Segmentation fault.
loop () at loop.c:1
1       void loop() { loop(); }
(gdb) where
#0  loop () at loop.c:1
#1  0x080483d8 in loop () at loop.c:1
#2  0x080483d8 in loop () at loop.c:1
#3  0x080483d8 in loop () at loop.c:1
#4  0x080483d8 in loop () at loop.c:1
#5  0x080483d8 in loop () at loop.c:1
#6  0x080483d8 in loop () at loop.c:1
#7  0x080483d8 in loop () at loop.c:1
#8  0x080483d8 in loop () at loop.c:1
#9  0x080483d8 in loop () at loop.c:1
以下省略



ということで、まあデバッガで見ればすぐわかるんですけどね。





話が脱線したついでに。
実は、最初、もっと単純に、こう書いて試してみました。



% cat loop2.c
int main( int c, char** v )
{
  main(c,v);
}



% gcc -O0 -o loop2 loop2.c



% gdb loop2
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd"...(no debugging symbols found)...
(gdb) run
Starting program: /home/nhh/loop2
(no debugging symbols found)...(no debugging symbols found)...
Program received signal SIGSEGV, Segmentation fault.
0x080483ea in main ()
(gdb) where
#0  0x080483ea in main ()



なんか、スタックフレームが増えていかないんですけど・・・どうしてかな。



(gdb) disas main
Dump of assembler code for function main:
0x080483d0 <main+0>:    lea    0x4(%esp),%ecx
0x080483d4 <main+4>:    and    $0xfffffff0,%esp
0x080483d7 <main+7>:    pushl  0xfffffffc(%ecx)
0x080483da <main+10>:   push   %ebp
0x080483db <main+11>:   mov    %esp,%ebp
0x080483dd <main+13>:   push   %ecx
0x080483de <main+14>:   sub    $0x8,%esp
0x080483e1 <main+17>:   mov    0x4(%ecx),%eax
0x080483e4 <main+20>:   mov    %eax,0x4(%esp)
0x080483e8 <main+24>:   mov    (%ecx),%eax
0x080483ea <main+26>:   mov    %eax,(%esp)
0x080483ed <main+29>:   call   0x80483d0 <main>
0x080483f2 <main+34>:   add    $0x8,%esp
0x080483f5 <main+37>:   pop    %ecx
0x080483f6 <main+38>:   pop    %ebp
0x080483f7 <main+39>:   lea    0xfffffffc(%ecx),%esp
0x080483fa <main+42>:   ret
0x080483fb <main+43>:   nop
0x080483fc <main+44>:   nop



(gdb) break main
Breakpoint 1 at 0x80483d0: file loop2.c, line 2.
(gdb) run
Starting program: /home/nhh/loop2



Breakpoint 1, main (c=Error accessing memory address 0x2: Bad address.
) at loop2.c:2
2       {
(gdb) info frame
Stack level 0, frame at 0xbfbfe740:
eip = 0x80483d0 in main (loop2.c:2); saved eip 0x8048359
source language c.
Arglist at 0xbfbfe738, args: c=Error accessing memory address 0x2: Bad address.
(gdb) si 100



Breakpoint 1, main (c=1, v=0xbfbfe764) at loop2.c:2
2       {
(gdb) info frame
Stack level 0, frame at 0xbfbfe71c:
eip = 0x80483d0 in main (loop2.c:2); saved eip 0x80483f2
source language c.
Arglist at 0xbfbfe714, args: c=1, v=0xbfbfe764
Locals at 0xbfbfe714, Previous frame's sp is 0xbfbfe71c
Saved registers:
  eip at 0xbfbfe718
(gdb) si 100



Breakpoint 1, main (c=1, v=0xbfbfe764) at loop2.c:2
2       {
(gdb) info frame
Stack level 0, frame at 0xbfbfe6fc:
eip = 0x80483d0 in main (loop2.c:2); saved eip 0x80483f2
source language c.
Arglist at 0xbfbfe6f4, args: c=1, v=0xbfbfe764
Locals at 0xbfbfe6f4, Previous frame's sp is 0xbfbfe6fc
Saved registers:
  eip at 0xbfbfe6f8
(gdb) info stack
#0  main (c=1, v=0xbfbfe764) at loop2.c:2



頭がぼんやりしてて、あのアセンブラコードで、何がどうなってんのか、ぜんぜん追いかけられないんですが、espをいじってるところが怪しい?





mainじゃない関数で、無限関数呼び出し。



% gcc -O0 -g loop.c -o loop
% gdb loop
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd"...
(gdb) break loop
Breakpoint 1 at 0x80483d3: file loop.c, line 1.
(gdb) run
Starting program: /home/nhh/loop



Breakpoint 1, loop () at loop.c:1
1       void loop() { loop(); }
(gdb) disas loop
Dump of assembler code for function loop:
0x080483d0 <loop+0>:    push   %ebp
0x080483d1 <loop+1>:    mov    %esp,%ebp
0x080483d3 <loop+3>:    call   0x80483d0 <loop>
0x080483d8 <loop+8>:    pop    %ebp
0x080483d9 <loop+9>:    ret
End of assembler dump.



(gdb) info frame
Stack level 0, frame at 0xbfbfe724:
eip = 0x80483d3 in loop (loop.c:1); saved eip 0x80483f3
called by frame at 0xbfbfe730
source language c.
Arglist at 0xbfbfe71c, args:
Locals at 0xbfbfe71c, Previous frame's sp is 0xbfbfe724
Saved registers:
  ebp at 0xbfbfe71c, eip at 0xbfbfe720
(gdb) si 100



Breakpoint 1, loop () at loop.c:1
1       void loop() { loop(); }
(gdb) info frame
Stack level 0, frame at 0xbfbfe71c:
eip = 0x80483d3 in loop (loop.c:1); saved eip 0x80483d8
called by frame at 0xbfbfe724
source language c.
Arglist at 0xbfbfe714, args:
Locals at 0xbfbfe714, Previous frame's sp is 0xbfbfe71c
Saved registers:
  ebp at 0xbfbfe714, eip at 0xbfbfe718
(gdb) si 100



Breakpoint 1, loop () at loop.c:1
1       void loop() { loop(); }
(gdb) info frame
Stack level 0, frame at 0xbfbfe714:
eip = 0x80483d3 in loop (loop.c:1); saved eip 0x80483d8
called by frame at 0xbfbfe71c
source language c.
Arglist at 0xbfbfe70c, args:
Locals at 0xbfbfe70c, Previous frame's sp is 0xbfbfe714
Saved registers:
  ebp at 0xbfbfe70c, eip at 0xbfbfe710
(gdb) info stack
#0  loop () at loop.c:1
#1  0x080483d8 in loop () at loop.c:1
#2  0x080483d8 in loop () at loop.c:1
#3  0x080483f3 in main () at loop.c:5



もうこんなの覚えてないよ・・・
まあ、いいか。



0 件のコメント:

コメントを投稿