|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2013-02-11 01:57 UTC] 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.
Patchestruncate_fix.diff (last revision 2013-02-11 10:04 UTC by nachms+php at gmail dot com)Pull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sun Oct 26 14:00:01 2025 UTC |
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.Also, I didn't mention, my program hangs waiting for FPM to close the connection with the count_bytes line commented out. With the count_bytes line, this is my output: FCGI_STDOUT: ====== X-Powered-By: PHP/5.4.11 Content-type: text/html Array ( [USER] => www-data [HOME] => /var/www [FCGI_ROLE] => RESPONDER [QUERY_STRING] => test [REQUEST_METHOD] => PUT [CONTENT_TYPE] => [CONTENT_LENGTH] => 4296015872 [SCRIPT_NAME] => /index.php [REQUEST_URI] => / [DOCUMENT_URI] => /index.php [DOCUMENT_ROOT] => /home/payden [SERVER_PROTOCOL] => HTTP/1.1 [GATEWAY_INTERFACE] => CGI/1.1 [SCRIPT_FILENAME] => /home/payden/index.php [SERVER_NAME] => test.localhost.net [HTTP_HOST] => test.localhost.net [REMOTE_ADDR] => 127.0.0.1 [REMOTE_PORT] => 12312 [SERVER_ADDR] => 127.0.0.1 [SERVER_PORT] => 80 [PHP_SELF] => /index.php [REQUEST_TIME_FLOAT] => 1361331663.522 [REQUEST_TIME] => 1361331663 [argv] => Array ( [0] => test ) [argc] => 1 ) Amount Read: 2147483647 ====== FCGI_END_REQUEST Connection closed. Actually sent: 2155288000 So, it finishes cleanly with stock 5.4.11, but the number of bytes actually sent before FPM closes the connection varies and the PHP side of things always reports max 32-bit signed int as amount read.Please try with a source build. I've no problems POSTing or PUTting files of size 10G. █ mike@smugmug:~/tmp$ l -hl 10G -rw-r--r-- 1 mike users 9.8G 1. Jul 21:44 10G █ mike@smugmug:~/tmp$ cat /srv/http/put.php <?php $tmp = tempnam("/var/tmp", "put"); var_dump(file_put_contents($tmp, fopen("php://input","r")), filesize($tmp), unlink($tmp)); █ mike@smugmug:~/build/php-5.6-dbg$ ./sapi/cgi/php-cgi -b 0:9999 -d post_max_size=99G -d upload_max_filesize=99G █ mike@smugmug:~/tmp$ curl -v --upload-file ~/tmp/10G http://localhost:88/put.php * Hostname was NOT found in DNS cache * Trying ::1... * connect to ::1 port 88 failed: Connection refused * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 88 (#0) > PUT /put.php HTTP/1.1 > User-Agent: curl/7.37.0 > Host: localhost:88 > Accept: */* > Content-Length: 10485760000 > Expect: 100-continue > < HTTP/1.1 100 Continue * We are completely uploaded and fine < HTTP/1.1 200 OK * Server nginx/1.6.0 is not blacklisted < Server: nginx/1.6.0 < Date: Wed, 16 Jul 2014 19:47:33 GMT < Content-Type: text/html; charset=UTF-8 < Transfer-Encoding: chunked < Connection: keep-alive < X-Powered-By: PHP/5.6.0-dev < int(10485760000) int(10485760000) bool(true) * Connection #0 to host localhost left intact