# HG changeset patch # User Dan # Date 1243365866 14400 # Node ID 32f6e2ee15ab67fe1e049350435bd3731347760a # Parent c4aefad02ce4815f9aed371fc7466aa4debe7008 WebServer: fixed longstanding non-security fwrite buffer overflow bug diff -r c4aefad02ce4 -r 32f6e2ee15ab multithreading.php --- a/multithreading.php Sun Jan 04 22:56:57 2009 -0500 +++ b/multithreading.php Tue May 26 15:24:26 2009 -0400 @@ -37,12 +37,13 @@ function Threader_SigUsr2() { - global $threader_instances; + global $threader_instances, $threader_notick; + if ( @$threader_notick ) + return; foreach ( $threader_instances as &$mt ) { if ( is_object($mt) ) { - $parchild = $mt->is_child() ? 'child' : 'parent'; $mt->event_sigusr2(); } } @@ -136,8 +137,8 @@ $threader_instances[] =& $this; + pcntl_signal(SIGCHLD, 'Threader_SigChld'); pcntl_signal(SIGUSR2, 'Threader_SigUsr2'); - pcntl_signal(SIGCHLD, 'Threader_SigChld'); } $this->json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); @@ -273,6 +274,7 @@ if ( $this->is_child() ) { // this is easy - the parent sent the signal. + @stream_set_blocking($this->parent_sock, 0); $command = rtrim(fgets($this->parent_sock, 102400), "\n"); } else @@ -281,8 +283,7 @@ // of time and try to read; if we get something, awesome. foreach ( $this->ipc_sockets as $pid => $socket ) { - // 1000 microseconds = 1/80th of the time it takes you to blink. - @stream_set_timeout($socket, 0, 1000); + @stream_set_blocking($socket, 0); $command = rtrim(@fgets($socket, 102400), "\n"); if ( !empty($command) ) { diff -r c4aefad02ce4 -r 32f6e2ee15ab webserver.php --- a/webserver.php Sun Jan 04 22:56:57 2009 -0500 +++ b/webserver.php Tue May 26 15:24:26 2009 -0400 @@ -334,6 +334,7 @@ $this->server_string = "PhpHttpd/" . HTTPD_VERSION . " PHP/" . PHP_VERSION . "\r\n"; $this->parent_pid = getmypid(); $this->threader = new Threader(); + $this->threader->ipc_register('ws_reboot', array(&$this, 'reboot')); // create a UUID $uuid_base = md5(microtime() . ( function_exists('mt_rand') ? mt_rand() : rand() )); @@ -351,7 +352,7 @@ function __destruct() { - if ( !is_object($this->threader) ) + if ( !$this->threader ) { return false; } @@ -437,6 +438,10 @@ } else if ( !$this->threader->is_child() && $oldfork ) { + // we are the parent and have been asked to respawn. there are (presumably) + // still children running around; when one of them dies, we'll receive a + // SIGCHLD, but often times this is already being called from the SIGUSR2 + // handler. whoops. if ( function_exists('status') ) status('Waiting on all children'); @@ -448,8 +453,7 @@ } else { - // this is a childless parent; delay any action until the current - // request has been sent (do nothing now) + // not sure what to do in this particular scenario. } } @@ -578,7 +582,7 @@ { if ( $start_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) || $remote->is_eof() ) { - // request expired -- end the process here + // request expired -- end the iteration here if ( !$this->threader->is_child() ) $remote->destroy(); @@ -1256,7 +1260,8 @@ // $output = dechex(strlen($output)) . "\r\n$output"; // write body - $socket->write($output); + if ( !empty($output) ) + $socket->write($output); $this->headers_sent = false; } @@ -2134,13 +2139,11 @@ function write($data) { - $data = str_split($data, 8096); - foreach ( $data as $chunk ) + $size = strlen($data); + $written = 0; + while ( $written < $size ) { - while ( !@fwrite($this->sock, $chunk) ) - { - usleep(50000); - } + $written += @fwrite($this->sock, substr($data, $written)); } return true; }