Edit report at https://bugs.php.net/bug.php?id=64187&edit=1
ID: 64187
Comment by: nachms+php at gmail dot com
Reported by: nachms+php at gmail dot com
Summary: CGI/FastCGI truncates input to modulo 4GB
Status: Open
Type: Bug
Package: Streams related
Operating System: Linux
PHP Version: 5.4.11
Block user comment: N
Private report: N
New Comment:
In cgi_main.c, in sapi_cgi_read_post() and sapi_fcgi_read_post(), I found if I
comment out the line: count_bytes = MIN(count_bytes, (uint)
SG(request_info).content_length - SG(read_post_bytes));
Then the bug is fixed.
I'm not sure why the amount of bytes going to be read is bounded to
read_post_bytes subtracted from the length. read() will only read the amount
that it can, and it doesn't matter if you asked for too much, it will return
what is available.
For FastCGI, I'm not familiar enough with fcgi_read() enough to know if the
lack of bounding causes a problem or not. However, mod_php5 as an example
doesn't bound it. And if bounding is needed, this code needs to make use of
long or unsigned long instead of int and unit.
Previous Comments:
------------------------------------------------------------------------
[2013-02-11 02:37:29] nachms+php at gmail dot com
I think I found the bug in cgi_main.c:
static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
{
uint read_bytes = 0;
int tmp_read_bytes;
count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length -
SG(read_post_bytes));
while (read_bytes < count_bytes) {
tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes,
count_bytes - read_bytes);
if (tmp_read_bytes <= 0) {
break;
}
read_bytes += tmp_read_bytes;
}
return read_bytes;
}
It looks like content_length, a long, is being truncated to a uint. I'll look
into fixing this based on what mod_php5 does.
------------------------------------------------------------------------
[2013-02-11 01:57:46] nachms+php at gmail dot com
Description:
------------
I've tested sending huge amounts of data via PUT to PHP from 5.3.x through
5.4.x via CGI, FastCGI, and mod_php for Apache, all on AMD64.
In all my tests, mod_php with Apache seems to be fine. However via CGI or
FastCGI, PHP can only see the amount of data modulo 4294967296. Which seems to
indicate that somewhere an int instead of a long is used in the the CGI
processing code, but so far, I have been unable to find where exactly. All my
builds are 64-bit, so that's not the issue.
To elaborate, via mod_php, if I send via HTTP PUT 4296015872 bytes, then PHP
will see all of them. However, via CGI or FastCGI, PHP will only see 1048576
bytes.
Test script:
---------------
<?php
print_r($_SERVER); //Print server variables so we can see Content-Length
$amount = 0;
$ifp = @fopen('php://input', 'rb');
if ($ifp)
{
while (!feof($ifp))
{
set_time_limit(0);
$buf = fread($ifp, 8192);
if ($buf !== false) { $amount += strlen($buf); }
}
fclose($tfp);
set_time_limit(0);
echo 'Amount Read: ', $amount, "\n";
}
//Test via HTTP (Apache):
http://paste.nachsoftware.com/Nach/TXmPR8289646bbb54bf40ce295115111acde1eYP
//Test via CGI (with FastCGI sharing results)
http://paste.nachsoftware.com/Nach/MXNpV4ec2e499fc0941773aff51184e6e618d2lN
Expected result:
----------------
When tested with either of my C test programs, I expect to see 4296015872
listed as the amount read.
Actual result:
--------------
With mod_php in Apache, I see 4296015872 which is correct. But with
CGI/FastCGI, I see 1048576 as the amount read, which is 4296015872%4294967296,
which indicates, somewhere a long is being converted to an int within the
CGI/FastCGI code.
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=64187&edit=1