Skip to content

Commit ca487ae

Browse files
committed
Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3: Fix memory leaks when returning refcounted value from curl callback
2 parents a68f1e7 + c6b058b commit ca487ae

File tree

5 files changed

+52
-6
lines changed

5 files changed

+52
-6
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ PHP NEWS
66
. Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction
77
order). (Daniil Gentili)
88

9+
- Curl:
10+
. Fix memory leaks when returning refcounted value from curl callback.
11+
(nielsdos)
12+
913
03 Jul 2025, PHP 8.4.9
1014

1115
- BcMath:

ext/curl/curl_private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ void _php_curl_multi_cleanup_list(void *data);
139139
void _php_curl_verify_handlers(php_curl *ch, bool reporterror);
140140
void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source);
141141

142+
/* Consumes `zv` */
143+
zend_long php_curl_get_long(zval *zv);
144+
142145
static inline php_curl *curl_from_obj(zend_object *obj) {
143146
return (php_curl *)((char *)(obj) - XtOffsetOf(php_curl, std));
144147
}

ext/curl/interface.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
600600
if (!Z_ISUNDEF(retval)) {
601601
_php_curl_verify_handlers(ch, /* reporterror */ true);
602602
/* TODO Check callback returns an int or something castable to int */
603-
length = zval_get_long(&retval);
603+
length = php_curl_get_long(&retval);
604604
}
605605

606606
zval_ptr_dtor(&argv[0]);
@@ -633,7 +633,7 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
633633
if (!Z_ISUNDEF(retval)) {
634634
_php_curl_verify_handlers(ch, /* reporterror */ true);
635635
/* TODO Check callback returns an int or something castable to int */
636-
rval = zval_get_long(&retval);
636+
rval = php_curl_get_long(&retval);
637637
}
638638
zval_ptr_dtor(&argv[0]);
639639
zval_ptr_dtor(&argv[1]);
@@ -670,7 +670,7 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
670670
if (!Z_ISUNDEF(retval)) {
671671
_php_curl_verify_handlers(ch, /* reporterror */ true);
672672
/* TODO Check callback returns an int or something castable to int */
673-
if (0 != zval_get_long(&retval)) {
673+
if (0 != php_curl_get_long(&retval)) {
674674
rval = 1;
675675
}
676676
}
@@ -708,7 +708,7 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
708708
if (!Z_ISUNDEF(retval)) {
709709
_php_curl_verify_handlers(ch, /* reporterror */ true);
710710
/* TODO Check callback returns an int or something castable to int */
711-
if (0 != zval_get_long(&retval)) {
711+
if (0 != php_curl_get_long(&retval)) {
712712
rval = 1;
713713
}
714714
}
@@ -807,6 +807,7 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key,
807807
}
808808
} else {
809809
zend_throw_error(NULL, "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH");
810+
zval_ptr_dtor(&retval);
810811
}
811812
}
812813

@@ -901,7 +902,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
901902
if (!Z_ISUNDEF(retval)) {
902903
// TODO: Check for valid int type for return value
903904
_php_curl_verify_handlers(ch, /* reporterror */ true);
904-
length = zval_get_long(&retval);
905+
length = php_curl_get_long(&retval);
905906
}
906907
zval_ptr_dtor(&argv[0]);
907908
zval_ptr_dtor(&argv[1]);
@@ -1322,6 +1323,17 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
13221323
(*source->clone)++;
13231324
}
13241325

1326+
zend_long php_curl_get_long(zval *zv)
1327+
{
1328+
if (EXPECTED(Z_TYPE_P(zv) == IS_LONG)) {
1329+
return Z_LVAL_P(zv);
1330+
} else {
1331+
zend_long ret = zval_get_long(zv);
1332+
zval_ptr_dtor(zv);
1333+
return ret;
1334+
}
1335+
}
1336+
13251337
static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */
13261338
{
13271339
struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;

ext/curl/multi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
410410
zval_ptr_dtor_nogc(&headers);
411411

412412
if (!Z_ISUNDEF(retval)) {
413-
if (CURL_PUSH_DENY != zval_get_long(&retval)) {
413+
if (CURL_PUSH_DENY != php_curl_get_long(&retval)) {
414414
rval = CURL_PUSH_OK;
415415
zend_llist_add_element(&mh->easyh, &pz_ch);
416416
} else {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Returning refcounted value from callback must not leak
3+
--EXTENSIONS--
4+
curl
5+
--FILE--
6+
<?php
7+
include 'server.inc';
8+
$host = curl_cli_server_start();
9+
10+
$url = "{$host}/get.inc";
11+
$ch = curl_init($url);
12+
13+
function return_non_interned_string() {
14+
return str_repeat('x', random_int(5, 5));
15+
}
16+
17+
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
18+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
19+
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'return_non_interned_string');
20+
curl_setopt($ch, CURLOPT_XFERINFOFUNCTION, 'return_non_interned_string');
21+
curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'return_non_interned_string');
22+
curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'return_non_interned_string');
23+
echo curl_exec($ch), PHP_EOL;
24+
echo "ok";
25+
?>
26+
--EXPECT--
27+
ok

0 commit comments

Comments
 (0)