From 3774d1a029b89b3552d7f0da9798a0ecbff50abd Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Tue, 21 Jul 2026 09:03:45 +0000 Subject: [PATCH 1/5] 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 e198b0bb7d77..61b0a652bab1 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2178,7 +2178,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); } @@ -2186,7 +2186,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 2b97ea91532cc7cb0d6c3221794b8f166bcb269e Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Tue, 21 Jul 2026 11:51:51 +0000 Subject: [PATCH 2/5] 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 61b0a652bab1..db3dd01b5505 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2178,7 +2178,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); } @@ -2186,7 +2186,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 43c3d7d7bba1207396292c50883a27794fe592aa Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Fri, 7 Aug 2026 18:47:55 +0000 Subject: [PATCH 3/5] 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 d000d645090a51fe2b911b427e122a05721c1d74 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Sat, 8 Aug 2026 16:32:23 +0000 Subject: [PATCH 4/5] 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=" From e21fd5d4e0de8586a063b8856ce89dc92e4a3086 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Sun, 16 Aug 2026 17:52:41 +0000 Subject: [PATCH 5/5] Add NEWS, use EXPECT instead of EXPECTF in test --- NEWS | 5 +++++ ext/curl/tests/curl_post_large_string.phpt | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 9977ea3b3409..a75d304276f3 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,11 @@ PHP NEWS class constants via OBJ->prop = $val). (Khaled Alam) . Reverted GH-22833, which attempted to fix bug GH-18985. (ilutov) +- Curl: + . Set content length using CURLOPT_POSTFIELDSIZE_LARGE instead of + CURLOPT_POSTFIELDSIZE. This makes it possible to post strings larger than + 2GB on some platforms, e.g. Windows. (Sjoerd Langkemper) + - DOM: . Fixed bug GH-22624 (use-after-free via DOMNameSpaceNode after DOMDocument::xinclude()). (David Carlier) diff --git a/ext/curl/tests/curl_post_large_string.phpt b/ext/curl/tests/curl_post_large_string.phpt index 526fc78dcbd8..b6bc67825428 100644 --- a/ext/curl/tests/curl_post_large_string.phpt +++ b/ext/curl/tests/curl_post_large_string.phpt @@ -26,5 +26,5 @@ $response = curl_exec($ch); var_dump($response); ?> ---EXPECTF-- +--EXPECT-- string(28) "Content-length: =2147483748="