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