| <?php | |
| if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) header('Last-Modified: '.$_SERVER['HTTP_IF_MODIFIED_SINCE']); | |
| echo str_repeat('x', (int) $_GET['len']); |
Event Timeline
Say we have any php fastcgi server (either HHVM or php-fpm), and we configure an apache vhost to do
ProxyPass /crash fastcgi://127.0.0.1:9000/my/docroot/apache_sucks.php retry=0
We can trigger the bug by calling
curl localhost/crash -v -H 'If-Modified-Since: Thu, 16 Oct 2014 09:12:36 GMT'
results in a log line as follows:
[Tue Dec 16 12:14:11.927301 2014] [proxy_fcgi:error] [pid 12871:tid 140613529855744] [client 127.0.0.1:58351] AH01070: Error parsing script headers
when the total response length exceeds 8192 bytes (so it's fragmented in more than one packet), e.g.:
curl localhost/crash?len=8081 -v -H 'If-Modified-Since: Thu, 16 Oct 2014 09:12:36 GMT'
it also causes curl to output this error:
* Excess found in a non pipelined read: excess = 373 url = /crash?len=8081 (zero-length body)
because the preset error page is returned as a body (!!!). In the logs we find
[Tue Dec 16 12:14:35.374423 2014] [proxy_fcgi:error] [pid 12872:tid 140613708728064] [client 127.0.0.1:58393] AH01070: Error parsing script headers [Tue Dec 16 12:14:35.374470 2014] [proxy_fcgi:error] [pid 12872:tid 140613708728064] [client 127.0.0.1:58393] AH01068: Got bogus version 1 [Tue Dec 16 12:14:35.374476 2014] [proxy_fcgi:error] [pid 12872:tid 140613708728064] (22)Invalid argument: [client 127.0.0.1:58393] AH01075: Error dispatching request to :
and in the access.log:
127.0.0.1 - - [16/Dec/2014:12:14:35 +0000] "GET /crash?len=8081 HTTP/1.1" 503 470 "-" "curl/7.35.0"
but the client received a "304 Not Modified" response, so most clients (and Varnish, luckily, among those) do the right thing.
In fact, if using telnet, we get:
HTTP/1.1 304 Not Modified Date: Tue, 16 Dec 2014 12:27:43 GMT Server: Apache/2.4.7 (Ubuntu) Connection: close <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>503 Service Unavailable</title> </head><body> <h1>Service Unavailable</h1> <p>The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.</p> <hr> <address>Apache/2.4.7 (Ubuntu) Server at hhvm-img.eqiad.wmflabs Port 80</address> </body></html> Connection closed by foreign host.
And we don't get the "error dispatching request" line, because telnet will not disconnect after the headers are received.
This is clearly a bug in how mod_proxy_fastcgi handles the parsing of fastcgi packets when the final response should be 304.