Edit report at https://bugs.php.net/bug.php?id=64187&edit=1
ID: 64187
Comment by: payden at paydensutherland 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:
I may be way off here, but from what I can see in SAPI.c (for 5.4.11, line 266 is
where the callback is invoked), count_bytes is the number of bytes that the
sapi_module_struct->read_post callback can safely stuff in the buffer without
overflowing its bounds. I think ignoring count_bytes in the callback is probably
a bad idea. Just my two cents. I'll be looking more into it and I'll post here
if I come up with a solution.
Previous Comments:
------------------------------------------------------------------------
[2013-02-11 19:27:39] nachms+php at gmail dot com
Due to lack of comments as mentioned above, I'm unsure what the problematic loop is needed for.
However thinking more about it, perhaps it's needed for pipelining or multiplexing?
In which case, changing read_post_bytes in SAPI.h from int to long, and removing the uint cast from
SG(request_info).content_length may be the correct solution.
------------------------------------------------------------------------
[2013-02-11 10:11:29] nachms+php at gmail dot com
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.
------------------------------------------------------------------------
[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