php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #26494 [PATCH] Timeout setting not honored while writing to a dead client
Submitted: 2003-12-01 17:58 UTC Modified: 2005-08-27 01:00 UTC
Votes:8
Avg. Score:4.9 ± 0.3
Reproduced:8 of 8 (100.0%)
Same Version:6 (75.0%)
Same OS:7 (87.5%)
From: hexer at studentcenter dot org Assigned:
Status: No Feedback Package: Apache related
PHP Version: 5CVS, 4CVS (2005-01-25) OS: *
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: hexer at studentcenter dot org
New email:
PHP Version: OS:

 

 [2003-12-01 17:58 UTC] hexer at studentcenter dot org
Description:
------------
Problem: On Apache 1.3.x, the compiled-in mod_php4 will cause stuck httpd processes while trying to send output from php to a dead client connection.

The Cause: mod_php4.c does not set the Apache Timeout timer in the send functions.

Solution: Apache recommends any modules that write to the client to use the timeout functions provided by the Apache API.

The following modification to mod_php4.c will fix the problem and make mod_php4 comply with Apache 1.3.x Timeout setting.


static int sapi_apache_ub_write(const char *str, uint str_length TSRMLS_DC)
{
    int ret=0;

    if (SG(server_context)) {

+        ap_hard_timeout("php send body", (request_rec *) SG(server_context));

        ret = rwrite(str, str_length, (request_rec *) SG(server_context));

+        ap_kill_timeout((request_rec *) SG(server_context));

    }
    if (ret != str_length) {
        php_handle_aborted_connection();
    }
    return ret;
}


Reproduce code:
---------------
This problem is easilly reproduced by simply breaking the connection with the server while trying to load any php-generated  page.

Expected result:
----------------
Apache process must abort processing the request if a single write operation exceeds the amount of seconds set in the Timeout setting in httpd.conf.

Actual result:
--------------
#0  0x420d368d in writev () from /lib/i686/libc.so.6
#1  0x0815fb08 in writev_it_all ()
#2  0x0815fe77 in large_write ()
#3  0x0815ff3b in ap_bwrite ()
#4  0x08172c10 in ap_rwrite ()
#5  0x080884da in sapi_apache_ub_write ()
#6  0x0809ab6f in php_ub_body_write_no_header (
    str=0x86484ec "XXXXXX"..., str_length=4144) at /usr/local/src/php-4.3.4/main/output.c:689
#7  0x0809a117 in php_end_ob_buffer (send_buffer=1 '\001', just_flush=1 '\001')
    at /usr/local/src/php-4.3.4/main/output.c:299
#8  0x0809ab39 in php_b_body_write (
    str=0x8666834 "XXXXXX", 
    str_length=51) at /usr/local/src/php-4.3.4/main/output.c:616
#9  0x08099dd2 in php_body_write (
    str=0x8666834 "XXXXXX", 
    str_length=51) at /usr/local/src/php-4.3.4/main/output.c:121
#10 0x080abe89 in zend_print_zval_ex (
    write_func=0x808ef40 <php_body_write_wrapper>, expr=0x85992ac, indent=0)
    at /usr/local/src/php-4.3.4/Zend/zend.c:211
#11 0x080abe2f in zend_print_zval (expr=0x85992ac, indent=0)
    at /usr/local/src/php-4.3.4/Zend/zend.c:192
---Type <return> to continue, or q <return> to quit---
#12 0x080abb70 in zend_print_variable (var=0x85992ac)
    at /usr/local/src/php-4.3.4/Zend/zend_variables.c:147
#13 0x080b64b5 in execute (op_array=0x8642fac)
    at /usr/local/src/php-4.3.4/Zend/zend_execute.c:1244
#14 0x080b8635 in execute (op_array=0x862bf24)
    at /usr/local/src/php-4.3.4/Zend/zend_execute.c:2181
#15 0x080acc03 in zend_execute_scripts (type=8, retval=0x0, file_count=3)
    at /usr/local/src/php-4.3.4/Zend/zend.c:884
#16 0x0808ffb3 in php_execute_script (primary_file=0xbffff650)
    at /usr/local/src/php-4.3.4/main/main.c:1729
#17 0x080ba6e2 in apache_php_module_main (r=0x8256564, display_source_mode=0)
    at /usr/local/src/php-4.3.4/sapi/apache/sapi_apache.c:54
#18 0x08089038 in send_php ()
#19 0x080890a3 in send_parsed_php ()
#20 0x0816109c in ap_invoke_handler ()
#21 0x08175b1f in process_request_internal ()
#22 0x08175b7e in ap_process_request ()
#23 0x0816cd55 in child_main ()
#24 0x0816cfb6 in make_child ()
#25 0x0816d2f5 in perform_idle_server_maintenance ()
#26 0x0816d914 in standalone_main ()
#27 0x0816df1a in main ()
#28 0x420158d4 in __libc_start_main () from /lib/i686/libc.so.6

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-12-02 15:06 UTC] hexer at studentcenter dot org
it seems that somewhere along the line, SIG_ALRM handler gets rerouted or disabled in one of the php extensions (I'm highly suspecting mysql)
since apache's timeout relies on SIG_ALRM anything within php that disables it will break the timeout mechanism.

The stuck processes abort normally when calling the handler directly from GDB, however when receiving SIG_ALRM nothing happens.

the following is the config string for php
./configure --with-mysql \
--with-xml \
--with-apache=../apache_1.3.29 \
--enable-track-vars \
--with-gd \
--enable-discard-path \
--enable-bcmath \
--enable-gd-native-tt \
--with-freetype-dir=/usr/local \
--with-png-dir=/usr/local \
--with-jpeg-dir=/usr/local \
--with-zlib=/usr/local \
--enable-apc

from all the included extensions mysql seems to be the only one with references to SIG_ALRM.

I will continue looking but currently the only solution that worked for me is setting the timeout on send's.

please let me know if you were able to reproduce this.
 [2003-12-02 21:52 UTC] iliaa@php.net
I am having some difficulty replacating the problem and have not been able to get it to happen YET. Meanwhile could you please see if the problem occurs with PHP is compiled with mysql support?
 [2005-01-11 20:28 UTC] rasmus@php.net
I haven't been able to reproduce this either even with MySQL enabled.
 [2005-01-11 21:50 UTC] hexer at studentcenter dot org
Ok here is a more detailed step-by-step on how to reproduce:

Use php 4.3.10 compiled-in with Apache 1.3.33

1. Compile Apache 1.3 with php-4.3.10 compiled-in:

In source tree:
./configure --prefix=/usr/local/apache

In PHP source tree:
./configure --with-mysql \
--with-xml \
--with-apache=../apache_1.3.33 \
--enable-track-vars \
--with-gd \
--enable-discard-path \
--enable-bcmath \
--enable-gd-native-tt \
--with-freetype-dir=/usr/local \
--with-png-dir=/usr/local \
--with-jpeg-dir=/usr/local \
--with-zlib=/usr/local

make
make install

In Apache source tree:
./configure \
--activate-module=src/modules/php4/libphp4.a \
--enable-module=php4 \
--enable-module=rewrite \
--enable-module=so \
--prefix=/usr/local/apache

make
make install


2. Edit httpd.conf and 
make sure you have the following enabled in httpd.conf:

TimeOut 60

AddModule mod_status.c

ExtendedStatus On

<Location /server-status>
    SetHandler server-status
    Order deny,allow
    Allow from all
</Location>



3. Create the test output script (test.php):

<?php
for($i = 0; $i < 1000000; $i++) {
        echo "XXXXXXXXXXXXXXXXXXXXXXXX<br>\n";
}
?>


4. On a different computer, open the browser and point it to
test.php, as soon as test.php starts loading unplug the
computer from the network, before it finishes loading.
This one's important. You have to make a true dead connection without the server knowing about it.
Make sure your network does not notify the server immediately that the connection was closed or there is a broken pipe etc.
(by default the Linux recv has a very long timeout)


5. Check the server-status page. You will see something like this:

0-1 4990 0/177/15106 W  0.45 9 0 0.0 0.19 43.85  x.x.x.x your.server.com GET /test.php

Note the 'W' under status in that particular process.

6. You will see the SS value start going way beyond the timeout setting. This _must_ happen unless the system has somehow managed to find out that the connection is broken.

If the process is not in W status and 60 seconds have not passed that means the system somehow found out that the connection is broken so the dead-connection
condition hasn't been simulated.


Also another good experiment would be to see if the TimeOut overrides max_execution_time. 
If in your scenario TimeOut does in fact work, it probably overrides the max_execution_time and causes the script to abort if it runs longer than TimeOut seconds.
 [2005-01-27 23:24 UTC] hexer at studentcenter dot org
Ok I tried it, same results. It's the problem in mod_php4.c
also just as a side note, our php is compiled-in to apache.
 [2005-02-03 04:13 UTC] sniper@php.net
Can you provide that patch somewhere in the web and paste the url here? Also, please make the patch unified diff (diff -u).

 [2005-02-03 19:11 UTC] hexer at studentcenter dot org
http://www.studentcenter.org/mod_php4.patch

let me know if you have any questions or need anything else
 [2005-08-19 11:55 UTC] sniper@php.net
Does it make any difference if you have PHP compiled as DSO?

 [2005-08-27 01:00 UTC] php-bugs at lists dot php dot net
No feedback was provided for this bug for over a week, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
 [2006-06-06 10:02 UTC] joce at presence-pc dot com
Hi,

I'm experiencing exactly the same issue here.
Is there any plan to include the patch in php ?

Regards,
  Jocelyn
 [2008-03-31 08:06 UTC] youza at post dot cz
Yes I have the same problem  - php-5.1.6 on  Fedore Core 5  and
php 4.4.7 on Fedora Core 4
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Dec 02 20:01:32 2024 UTC