From c48fc1e7ab55677833d0e1c4082689154aee769a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 5 Aug 2026 22:20:58 +0200 Subject: [PATCH 1/2] run-tests: Improve handling of test reproduction helpers (#23066) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * run-tests: Add `strace` sub command for generated test reproduction scripts Co-authored-by: Tim Düsterhus * run-tests: Pass all provided arguments to `valgrind` * run-tests: Pass all provided arguments to `gdb` * run-tests: Pass all provided arguments to `lldb` * run-tests: `exec` into test reproduction helpers This avoids needlessly carrying around the shell and provides more direct access to the running executable. --------- Co-authored-by: Derick Rethans --- run-tests.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/run-tests.php b/run-tests.php index 89bd8ffb797c..89b5882f6674 100755 --- a/run-tests.php +++ b/run-tests.php @@ -2920,19 +2920,27 @@ function run_test(string $php, $file, array $env): string {$exported_environment} case "$1" in "gdb") - gdb -ex 'unset environment LINES' -ex 'unset environment COLUMNS' --args {$orig_cmd} + shift + exec gdb -ex 'unset environment LINES' -ex 'unset environment COLUMNS' "$@" --args {$orig_cmd} ;; "lldb") - lldb -- {$orig_cmd} + shift + exec lldb "$@" -- {$orig_cmd} ;; "valgrind") - USE_ZEND_ALLOC=0 valgrind $2 {$orig_cmd} + export USE_ZEND_ALLOC=0 + shift + exec valgrind "$@" {$orig_cmd} + ;; +"strace") + shift + exec strace "$@" {$orig_cmd} ;; "rr") - rr record $2 {$orig_cmd} + exec rr record $2 {$orig_cmd} ;; *) - {$orig_cmd} + exec {$orig_cmd} ;; esac SH; From ac37a9760b8fffa9a3e86d71737903efdf480e59 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Wed, 5 Aug 2026 21:59:28 +0100 Subject: [PATCH 2/2] ext/curl: error when curl read func returns unexpected long Raise a value error when the callback registered with CURLOPT_READFUNCTION returns an unexpected long. The function registered with CURLOPT_READFUNCTION should return a string. PHP then writes that string to a buffer and returns the length, so that curl can read that many bytes from the buffer. The function can also return CURL_READFUNC_ABORT and CURL_READFUNC_PAUSE, so it also supports returning longs. However, when it returns a long other than these two constants, it is interpreted as a length. PHP does not update the buffer, but does instruct curl it can read that many bytes from the buffer. It reads whatever uninitialized data that is in the buffer and sends it over the line to the server. This seems bad, so validate the return value of the read function and raise an error. Returning 0 is a bit of an edge case. It is not documented but does results in correct behavior (i.e. end-of-file). So we accept that, but don't advertise it as valid in the error message. Related to https://github.com/php/php-src/issues/10270 Close GH-22757 --- NEWS | 4 +++ UPGRADING | 6 ++++ ext/curl/curl.stub.php | 5 ++++ ext/curl/curl_arginfo.h | 3 +- ext/curl/interface.c | 8 ++++- .../curl_read_function_error_on_int.phpt | 30 +++++++++++++++++++ ext/curl/tests/curl_readfunc_abort.phpt | 21 +++++++++++++ 7 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 ext/curl/tests/curl_read_function_error_on_int.phpt create mode 100644 ext/curl/tests/curl_readfunc_abort.phpt diff --git a/NEWS b/NEWS index 0f3ba67434dc..96895dc4a4bc 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Changed run-tests.php to run test subprocesses without a shell where possible. (NickSdot) +- Curl: + . Raise a value error when the callback registered with CURLOPT_READFUNCTION + returns an unexpected long. (Sjoerd Langkemper) + - Date: . Update timelib to 2026.01. (Derick, timwolla) diff --git a/UPGRADING b/UPGRADING index ced365480707..453bae75701b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -24,6 +24,11 @@ PHP 8.6 UPGRADE NOTES has materialised the property by writing into the property table. The freshly-written value is returned directly. isset() is unaffected. +- Curl: + . The callback registered with CURLOPT_READFUNCTION now throws a ValueError + when returning an integer other than 0, CURL_READFUNC_ABORT or + CURL_READFUNC_PAUSE. + - COM . It is no longer possible to clone variant objects, this is because the cloning behaviour was ill defined. @@ -602,6 +607,7 @@ PHP 8.6 UPGRADE NOTES . CURL_SEEKFUNC_OK. . CURL_SEEKFUNC_FAIL. . CURL_SEEKFUNC_CANTSEEK. + . CURL_READFUNC_ABORT. - OpenSSL: . OPENSSL_RSA_PSS_SALTLEN_DIGEST. diff --git a/ext/curl/curl.stub.php b/ext/curl/curl.stub.php index 70e87cc9b146..6953f0e97cbc 100644 --- a/ext/curl/curl.stub.php +++ b/ext/curl/curl.stub.php @@ -1788,6 +1788,11 @@ * @cvalue CURLPAUSE_SEND_CONT */ const CURLPAUSE_SEND_CONT = UNKNOWN; +/** + * @var int + * @cvalue CURL_READFUNC_ABORT + */ +const CURL_READFUNC_ABORT = UNKNOWN; /** * @var int * @cvalue CURL_READFUNC_PAUSE diff --git a/ext/curl/curl_arginfo.h b/ext/curl/curl_arginfo.h index f2929f60c4e2..ea354d16df56 100644 --- a/ext/curl/curl_arginfo.h +++ b/ext/curl/curl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit curl.stub.php instead. - * Stub hash: d55adb230c533f4dde05e95759477dd9e1dd6efb */ + * Stub hash: 5da31d6790f9db408cac4aed3f81f7affb2849a6 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) @@ -574,6 +574,7 @@ static void register_curl_symbols(int module_number) REGISTER_LONG_CONSTANT("CURLPAUSE_RECV_CONT", CURLPAUSE_RECV_CONT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLPAUSE_SEND", CURLPAUSE_SEND, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLPAUSE_SEND_CONT", CURLPAUSE_SEND_CONT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_READFUNC_ABORT", CURL_READFUNC_ABORT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURL_READFUNC_PAUSE", CURL_READFUNC_PAUSE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURL_SEEKFUNC_OK", CURL_SEEKFUNC_OK, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURL_SEEKFUNC_FAIL", CURL_SEEKFUNC_FAIL, CONST_PERSISTENT); diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 07e53dfe0f9f..6df7cf66fbe7 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -819,7 +819,13 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx) length = MIN(nmemb, Z_STRLEN(retval)); memcpy(data, Z_STRVAL(retval), length); } else if (Z_TYPE(retval) == IS_LONG) { - length = Z_LVAL_P(&retval); + zend_long long_rv = Z_LVAL_P(&retval); + if (long_rv == 0 || long_rv == CURL_READFUNC_ABORT || long_rv == CURL_READFUNC_PAUSE) { + length = (size_t) long_rv; + } else { + zend_value_error("The CURLOPT_READFUNCTION callback must return a string or CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE"); + length = CURL_READFUNC_ABORT; + } } // TODO Do type error if invalid type? zval_ptr_dtor(&retval); diff --git a/ext/curl/tests/curl_read_function_error_on_int.phpt b/ext/curl/tests/curl_read_function_error_on_int.phpt new file mode 100644 index 000000000000..30ba97737727 --- /dev/null +++ b/ext/curl/tests/curl_read_function_error_on_int.phpt @@ -0,0 +1,30 @@ +--TEST-- +error when CURLOPT_READFUNCTION returns an integer +--EXTENSIONS-- +curl +--FILE-- + 'f']); +curl_setopt($ch, CURLOPT_TIMEOUT, 2); +curl_setopt($ch, CURLOPT_READFUNCTION, "custom_readfunction" ); + +try { + curl_exec($ch); +} catch (ValueError $e) { + echo $e->getMessage() . "\n"; +} +var_dump(curl_error($ch)); +?> +--EXPECT-- +The CURLOPT_READFUNCTION callback must return a string or CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE +string(29) "operation aborted by callback" diff --git a/ext/curl/tests/curl_readfunc_abort.phpt b/ext/curl/tests/curl_readfunc_abort.phpt new file mode 100644 index 000000000000..39103904fdc5 --- /dev/null +++ b/ext/curl/tests/curl_readfunc_abort.phpt @@ -0,0 +1,21 @@ +--TEST-- +Returning CURL_READFUNC_ABORT aborts the transfer +--EXTENSIONS-- +curl +--FILE-- + +--EXPECT-- +No output expected, because transfer was aborted by read function.