Description
Description
Description
We have a PHP extension that overwrites the function handler for curl_exec()
in the function table CG(function_table)
and during some testing we found that the execute_data
pointer passed to the function handler becomes NULL midway, before the function handler finishes execution.
Our custom function handler wrapper_curl_exec()
roughly does this:
void ZEND_FASTCALL wrapper_curl_exec(zend_execute_data *execute_data, zval *return_value)
{
// access execute_data -- pointer is valid
// call original function handler of curl_exec()
// access execute_data -- pointer is NULL, our extension crashes without a NULL check
// return
}
This does not happen always, but it is frequent enough on PHP 8.4.5 with our particular test setup and test case.
Test Setup
OS: Windows 11
PHP 8.4.5
PHP-CGI SAPI
Ngnix with 5 php-cgi.exe processes as a pool
Test Case
We are running curl -v http://localhost/main.php
in a loop with 0.5 secs delay from powershell
CLI.
main.php
sends a downstream HTTP request via curl_exec()
and downstream.php
sends another downstream request via curl_exec()
. final.php
just returns some dummy json data as a response. (See the reference scripts the end).
Observation
This curl loop from command line works for some time before seemingly taking too long for each request. At some point, we see our extension crashing because execute_data
pointer became NULL after the original curl_exec()
function returned (inside our extension) for one of the curl_exec()
calls.
A related observation is that even with our extension disabled, this curl loop starts taking too long after a while i.e., the curl_exec() in main.php
and downstream.php
take a long time. Sometimes nginx
throws 'Bad Gatewayor
Page not available` errors.
This issue also occurs with PHP 8.3 and PHP 8.2 but far less frequently.
Questions
- Why does
execute_data
pointer become NULL before the function handler returns? - Are there any valid/predictable scenarios where we should expect
execute_data
pointer to be NULL or become null in a function handler? This will allow us to decide if we have to pre-emptively keep a NULL check forexecute_data
in all our custom function handlers.
Let me know if you need any further details.
Reference
(relevant snippets)
# file: main.php
<?php
$ch = curl_init('http://localhost/downstream.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array_map(function($key, $value) {
return "$key: $value";
}, array_keys($phpinfo_req_headers), $phpinfo_req_headers));
$response = curl_exec($ch);
curl_close($ch);
# file: downstream.php
<?php
$ch = curl_init('http://localhost/final.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array_map(function($key, $value) {
return "$key: $value";
}, array_keys($phpinfo_req_headers), $phpinfo_req_headers));
$response = curl_exec($ch);
curl_close($ch);
PHP Version
PHP 8.4.5, PHP 8.3.17, PHP 8.2.27
Operating System
Windows 11