Skip to content

'zend_execute_data' pointer becomes NULL inside internal function handler of 'curl_exec()' #18216

Closed as not planned
@sjayexec

Description

@sjayexec

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

  1. Why does execute_data pointer become NULL before the function handler returns?
  2. 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 for execute_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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions