[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 メーリングリストの案内