[PHP-dev 875]ソケットへのfwriteで無限ループ
Takahiro Takano
takanota @ alpha.co.jp
2003年 8月 28日 (木) 11:11:36 JST
始めまして、鷹野と申します。
fsockopen で接続したリモートホストに対して、fwrite 前に切断された場合に、
fwrite 内で無限ループに陥る現象に遭遇しましたので、報告します。
ローカル:
Red Hat Linux 7.2 (kernel 2.4.7-10)
PHP 4.3.3(CLI)
リモート:
Windows XP professional
Apache 1.3.28
で確認しました。
PHP は php-4.3.3.tar.bz2 を元にコンパイルしたものを使用してます。
./configure \
--prefix=/usr/local/php \
--disable-all \
--with-apxs=/usr/local/apache/bin/apxs \
--with-pcre-regex \
--enable-mbstring \
--enable-mbregex \
--enable-posix \
--enable-memory-limit
再現手順:
----
<?php
$sock = fsockopen('10.XXX.XXX.XXX', 80);
if ($sock) {
echo "connected\n";
sleep(10);
$result = fwrite($sock, "GET / HTTP/1.0\r\n\r\n");
var_dump($result);
fclose($sock);
}
----
上記プログラムを実行し、"connected" と表示されてから10秒以内
(sleep している間)に、リモートの Apache (2つあるうち PID の小さい方)
をタスクマネージャ上で強制終了させる。
# これで、fwirte 前に リモートホストからの RSH パケットを受信すると思い
# ますが send() が -1 を返す状況を作り出せれば何でもいいと思います。
----
[takanota php]$ php -n -d error_reporting=2047 socket_test.php
connected
Notice: fwrite(): send of 18 bytes failed with errno=104 Connection reset by peer in /home/takanota/work/php/socket_test.php on line 7
Notice: fwrite(): send of 19 bytes failed with errno=32 Broken pipe in /home/takanota/work/php/socket_test.php on line 7
Notice: fwrite(): send of 20 bytes failed with errno=32 Broken pipe in /home/takanota/work/php/socket_test.php on line 7
Notice: fwrite(): send of 21 bytes failed with errno=32 Broken pipe in /home/takanota/work/php/socket_test.php on line 7
Notice: fwrite(): send of 22 bytes failed with errno=32 Broken pipe in /home/takanota/work/php/socket_test.php on line 7
(...以下、bytes の値を +1 しながら延々と Notice...)
----
PHP のソースを眺めたところ、php_sockop_write() 内(main/network.c:939)で
- send() の結果が 0 以下の時に Notice を発生させていること
- php_sockop_write() は常に send() の結果を返していること
が判りました。
この send() が -1 を返した場合、php_sockop_write() は (size_t)-1 を返す
ことになるので、php_sockop_write() の呼び出し元(main/stream.c:914)で
「php_sockop_write() は 4,294,967,295 バイトを送信して正常終了した」
と判断してしまう様で、while ループを抜けません...
手元の環境では 以下のパッチをあてることで php_sockop_write() が 0 で終了
し、無限ループが回避できることは確認しました。
# 自分の判断でソースに手をつけるのが始めてで、これでよいのかどうかが
# まったく判らないので、ご意見下さい。
以下、今回作成したパッチになります(単純な return 追加です)。
diff -ur php-4.3.3.orig/main/network.c php-4.3.3/main/network.c
--- php-4.3.3.orig/main/network.c Sat Jun 28 01:42:51 2003
+++ php-4.3.3/main/network.c Thu Aug 28 10:23:34 2003
@@ -939,6 +939,8 @@
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %d bytes failed with errno=%d %s",
count, php_socket_errno(), estr);
efree(estr);
+
+ return 0;
}
}
----
鷹野 貴弘 <takanota @ alpha.co.jp>
PHP-dev メーリングリストの案内