-
Notifications
You must be signed in to change notification settings - Fork 8k
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 GatewayorPage not available` errors.
This issue also occurs with PHP 8.3 and PHP 8.2 but far less frequently.
Questions
- Why does
execute_datapointer become NULL before the function handler returns? - Are there any valid/predictable scenarios where we should expect
execute_datapointer 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_datain 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