2004年5月13日木曜日

解説しよう!@PHP



http://bugs.php.net/bug.php?id=28374


このパッチについて。FreeBSD(or other unix??)+PostgreSQL+PHPな人でhttpdのCPU負荷が100%近くになってるひとに効くかもしれないパッチれす。


なんだか私が投稿し9分後にCVSにコミットしたのとメールが来たのでたぶん検証もなにもせずにつっこまれてる。ので未だにこいつが本当にhttpdの無限ループの犯人という確証ないんだけど。(ソース追えよ・・・俺)


取り合えず無限ループに陥ってるプロセスをgdbにアタッチしてスタックトレースすると。以下ののような感じ。(アタッチした時の都合でシンボルは自動では解決できなかったので、該当アドレスのバイナリコードとojbdump -s libphp.soを比較して推定したもの)



>|


#0 0x283fb4f3 in ?? xbuf_format_converter()


#1 0x283fc1a5 in ?? vspprintf()


#2 0x283f829b in ?? php_error_cb()


#3 0x284253a6 in ?? zend_error()


#4 0x2841d392 in ?? zend_timeout()


#5 0xbfbfffac in ?? ()//stackでのとらんぽりんこーど(シグナル?)


#6 0x283fc1a5 in ?? vspprintf()


#7 0x283f829b in ?? php_error_cb()


#8 0x284253a6 in ?? zend_error()


#9 0x2841d392 in ?? zend_timeout()


#10 0xbfbfffac in ?? ()//stackでのとらんぽりんこーど(シグナル?)


#11 0x283fc1a5 in ?? vspprintf()


#12 0x283f829b in ?? php_error_cb()


#13 0x284253a6 in ?? zend_error()


#14 0x2841d392 in ?? zend_timeout()


#15 0xbfbfffac in ?? ()


#16 0xbfbfffac in ?? ()


#17 0xbfbfffac in ?? ()


#18 0x2837750f in ?? _php_pgsql_trim_message()


#19 0x2837889b in ?? php_pgsql_get_link_info()


#20 0x28378a11 in ?? zif_pg_last_error()


#21 0x28437b80 in ?? execute()


#22 0x28437cfe in ?? execute()//アドレスがちがうのはさいてきかのせい?それともモ ジュール違い?


#23 0x28437cfe in ?? execute()


#24 0x28437cfe in ?? ()


#25 0x28437cfe in ?? ()


#26 0x28425753 in ?? zend_execute_scripts()


#27 0x283fa7a9 in ?? php_execute_script()


#28 0x2843e3f2 in ?? apache_php_module_main()


#29 0x2843f03b in ?? send_php()


#30 0x2843f0a4 in ?? send_parsed_php()


#31 0x8053f75 in ap_invoke_handler (r=0x8142038) at http_config.c:518


#32 0x80648d4 in process_request_internal (r=0x8142038) at http_request.c:1332


#33 0x8064935 in ap_process_request (r=0x8142038) at http_request.c:1348


#34 0x805d896 in child_main (child_num_arg=135536696) at http_main.c:4719


#35 0x805dacd in make_child (s=0x736ea3, slot=3, now=7564963) at http_main.c:4898


#36 0x805dd59 in perform_idle_server_maintenance () at http_main.c:5083


#37 0x805e259 in standalone_main (argc=2, argv=0xbfbffdd8) at http_main.c:5333


#38 0x805e79b in main (argc=2, argv=0xbfbffdd8) at http_main.c:5601




怪しいのは、php_pgsql_trim_mesageですな。トランポリンコードの直前ってことは多分Watch dog timerがphp_pgsql_trim_message実行中にかかったとおもうのねん。

さて該当ソース/usr/ports/lang/php4/work/php-4.3.6/ext/pgsql/pgsql.cを読んでみると。


218 static char * _php_pgsql_trim_message(const char *message, int *len)


219 {


220 register int i = strlen(message)-1;


221


222 if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') &&


message[i] == '.') {


223 --i;


224 }


225 while (i && (message[i] == '\r' || message[i] == '\n')) {


226 --i;


227 }


228 ++i;


229 if (len) {


230 *len = i;


231 }


232 return estrndup(message, i);


233 }




取り合えずstrlen()が0のときにi= -1・・・・いやな感じ。つうか225行目を見てみよう。非常にだめげなきがしますね。たまたまスタックに'r'か'n'が積まれている長さ0の文字列きたらすげーいやげなのですが・・・。取り合えず一つだけ'r'があったと仮定しておっかけましょう。
232行目でestrndup("",-1)を実行することになりますね。
そこでzend_alloc.cを眺めてみると。


376 ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)


377 {


378 char *p;


379


380 HANDLE_BLOCK_INTERRUPTIONS();


381 p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);


382 if (!p) {


383 HANDLE_UNBLOCK_INTERRUPTIONS();


384 return (char *)NULL;


385 }


386 HANDLE_UNBLOCK_INTERRUPTIONS();


387 memcpy(p, s, length);


388 p[length] = 0;


389 return p;


390 }




_emallocの仕様くわしくしらないけどlength-1がはいると長さ0のメモリを素直に確保してくれそうなよかーん。もしそうならmemcpyのところみてみると。lengthとして-1を渡してるので。この時点でセグフォがでてよさそうなんだけどなぁ・・なんらかの理由でスタックが破壊されてプロセスが暴走してたりするのかなぁ?(ヒープかもしれないけど)つーてーとトランポリンコードっておもっていたのは破壊されたスタックorメモリ上で動いていたなにか?

まっ。そのへんの謎はのこったものの、while(i~~のところをwhile(i>0に書き換えたらhttpdの無限ループはなおったからいいか。



0 件のコメント:

コメントを投稿