diff --git a/NEWS b/NEWS index e7f45c810b23..7d3e5ea0ecba 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,10 @@ PHP NEWS . Fix GH-21999: GC inconsistency with lazy object, var_dump(), and object comparison. (Arnaud) +- Curl: + . Fixed a leaked CurlHandle and clone accounting when a CURLMOPT_PUSHFUNCTION + callback denies the push or throws. (Ilia Alshanetsky) + - DOM: . Fixed use-after-free when re-constructing a DOMXPath whose php:function registrations are freed while still reachable from the cycle collector. diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 336a89a214c4..d51099e92ecb 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2834,7 +2834,7 @@ static void curl_free_obj(zend_object *object) fprintf(stderr, "DTOR CALLED, ch = %x\n", ch); #endif - if (!ch->cp) { + if (!ch->clone) { /* Can happen if constructor throws. */ zend_object_std_dtor(&ch->std); return; diff --git a/ext/curl/multi.c b/ext/curl/multi.c index a484373c107b..cdcfdc2418b1 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -409,16 +409,15 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea zend_call_known_fcc(&mh->handlers.server_push, &retval, /* param_count */ 3, call_args, /* named_params */ NULL); zval_ptr_dtor_nogc(&headers); - if (!Z_ISUNDEF(retval)) { - if (CURL_PUSH_DENY != php_curl_get_long(&retval)) { - rval = CURL_PUSH_OK; - zend_llist_add_element(&mh->easyh, &pz_ch); - } else { - /* libcurl will free this easy handle, avoid double free */ - ch->cp = NULL; - } + if (!Z_ISUNDEF(retval) && CURL_PUSH_DENY != php_curl_get_long(&retval)) { + rval = CURL_PUSH_OK; + zend_llist_add_element(&mh->easyh, &pz_ch); + return rval; } + ch->cp = NULL; + zval_ptr_dtor(&pz_ch); + return rval; } /* }}} */ diff --git a/ext/curl/tests/curl_pushfunction_deny_leak.phpt b/ext/curl/tests/curl_pushfunction_deny_leak.phpt new file mode 100644 index 000000000000..f10d158c871d --- /dev/null +++ b/ext/curl/tests/curl_pushfunction_deny_leak.phpt @@ -0,0 +1,80 @@ +--TEST-- +CURLMOPT_PUSHFUNCTION deny must not leak the pushed handle +--EXTENSIONS-- +curl +--SKIPIF-- + +--FILE-- + $iterations * $bytes_per_iteration_limit) { + printf("leaked %d bytes (%d bytes/iteration)\n", $growth, (int) ($growth / $iterations)); +} else { + echo "no leak detected\n"; +} +?> +--EXPECT-- +all pushes denied +no leak detected diff --git a/ext/curl/tests/curl_pushfunction_deny_retain.phpt b/ext/curl/tests/curl_pushfunction_deny_retain.phpt new file mode 100644 index 000000000000..4f33f2ca3911 --- /dev/null +++ b/ext/curl/tests/curl_pushfunction_deny_retain.phpt @@ -0,0 +1,57 @@ +--TEST-- +CURLMOPT_PUSHFUNCTION deny with the pushed handle retained by the callback +--EXTENSIONS-- +curl +--SKIPIF-- + +--FILE-- + strlen($data))); +unset($kept); +gc_collect_cycles(); +echo "Done\n"; +?> +--EXPECT-- +int(1) +bool(true) +Done