From 6c3ba3322421567941cd2734cd6b6599cab045f6 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Tue, 21 Jul 2026 09:03:45 +0000 Subject: [PATCH 1/4] Set curl post size using CURLOPT_POSTFIELDSIZE_LARGE This is a 64-bit number on all platforms. This improves support of posting files larger than 2GB. - https://curl.se/libcurl/c/CURLOPT_POSTFIELDSIZE.html - https://curl.se/libcurl/c/CURLOPT_POSTFIELDSIZE_LARGE.html --- ext/curl/interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index d5d20d825652..17b381127716 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2170,7 +2170,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue /* no need to build the mime structure for empty hashtables; also works around https://github.com/curl/curl/issues/6455 */ curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0L); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE_LARGE, 0L); } else { return build_mime_structure_from_hash(ch, zvalue); } @@ -2178,7 +2178,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue zend_string *tmp_str; zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */ - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str)); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE_LARGE, ZSTR_LEN(str)); error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str)); zend_tmp_string_release(tmp_str); } From 65ae274000b0e4d8352aa290ce442def7682c8bf Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Tue, 21 Jul 2026 11:51:51 +0000 Subject: [PATCH 2/4] Explicitly cast to curl_off_t --- ext/curl/interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 17b381127716..c9077970bdc0 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2170,7 +2170,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue /* no need to build the mime structure for empty hashtables; also works around https://github.com/curl/curl/issues/6455 */ curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE_LARGE, 0L); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t) 0); } else { return build_mime_structure_from_hash(ch, zvalue); } @@ -2178,7 +2178,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue zend_string *tmp_str; zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */ - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE_LARGE, ZSTR_LEN(str)); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t) ZSTR_LEN(str)); error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str)); zend_tmp_string_release(tmp_str); } From 4fe40c1353757dcb7a426fa623c2d6f50de99855 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Fri, 7 Aug 2026 18:47:55 +0000 Subject: [PATCH 3/4] Add unit test that posts a large payload --- ext/curl/tests/curl_post_large_string.phpt | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 ext/curl/tests/curl_post_large_string.phpt diff --git a/ext/curl/tests/curl_post_large_string.phpt b/ext/curl/tests/curl_post_large_string.phpt new file mode 100644 index 000000000000..af3b8f53d992 --- /dev/null +++ b/ext/curl/tests/curl_post_large_string.phpt @@ -0,0 +1,64 @@ +--TEST-- +CURL post data larger than 2GB (to test CURLOPT_POSTFIELDSIZE_LARGE) +--INI-- +memory_limit=3G +post_max_size=3G +--SKIPIF-- + +--EXTENSIONS-- +curl +--FILE-- + +--EXPECT-- +int(2147483748) +int(2147483748) From d0b8f64b089c85be64dfdc8335b0e63cf7061205 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Sat, 8 Aug 2026 16:32:23 +0000 Subject: [PATCH 4/4] Post to caddy The test (curl_post_large_string) posts data slightly bigger than fits in a signed 32-bit number. It uses Caddy and not server.inc, because PHP has limits on maximum upload size. It doesn't actually check whether it uploads the correct number of bytes, but it does check the Content-Length header, which is sufficient indication whether curl understands the correct size. --- ext/curl/tests/curl_post_large_string.phpt | 60 +++++----------------- 1 file changed, 13 insertions(+), 47 deletions(-) diff --git a/ext/curl/tests/curl_post_large_string.phpt b/ext/curl/tests/curl_post_large_string.phpt index af3b8f53d992..526fc78dcbd8 100644 --- a/ext/curl/tests/curl_post_large_string.phpt +++ b/ext/curl/tests/curl_post_large_string.phpt @@ -2,63 +2,29 @@ CURL post data larger than 2GB (to test CURLOPT_POSTFIELDSIZE_LARGE) --INI-- memory_limit=3G -post_max_size=3G --SKIPIF-- --EXTENSIONS-- curl --FILE-- true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $data, +]); -if (pcntl_fork()) { - // we don't use server.inc because that has limits on post size +$response = curl_exec($ch); +var_dump($response); - $conn = stream_socket_accept($socket); - - do { - $header = fgets($conn); - if (preg_match('~Content-Length: (\d+)~', $header, $matches)) { - $length = $matches[1]; - } - } while (trim($header)); - - $postdata = stream_get_contents($conn, $length); - var_dump(strlen($postdata)); - - fwrite($conn, "HTTP/1.1 204 No Content\r\n\r\n"); - fclose($conn); - - $status = 0; - pcntl_wait($status); -} else { - $size = 2 ** 31 + 100; // a little bit more than a signed 32-bit int */ - $data = str_repeat('a', $size); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:29999/"); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POST, 1); - - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - - $response = curl_exec($ch); - - $uploaded_size = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD_T); - var_dump($uploaded_size); -} - -fclose($socket); ?> ---EXPECT-- -int(2147483748) -int(2147483748) +--EXPECTF-- +string(28) "Content-length: =2147483748="