Skip to content

Commit 9f56efe

Browse files
committed
Free unbound arguments when partial application construction throws
The ZEND_CALLABLE_CONVERT_PARTIAL handler relies on zp_bind() to move each pre-bound argument into the partial and never frees positional arguments itself. When construction throws before an argument is moved, a NULL op_array from zp_get_op_array() or a bound-argument type error, the unmoved arguments were owned by nobody and leaked. Free the arguments that were not moved at each throwing path.
1 parent dec19ed commit 9f56efe

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
Partial application must not leak bound arguments when construction throws
3+
--FILE--
4+
<?php
5+
class Obj {}
6+
7+
function typed(int $a, $b, $c) {}
8+
try {
9+
typed([1, 2, 3], new Obj(), ?);
10+
} catch (\TypeError $e) {
11+
echo $e::class, "\n";
12+
}
13+
14+
function typed2($a, int $b, $c, $d) {}
15+
try {
16+
typed2(new Obj(), [1, 2], new Obj(), ?);
17+
} catch (\TypeError $e) {
18+
echo $e::class, "\n";
19+
}
20+
21+
function one($a) {}
22+
try {
23+
one(new Obj(), new Obj(), ?);
24+
} catch (\ArgumentCountError $e) {
25+
echo $e::class, "\n";
26+
}
27+
28+
echo "OK\n";
29+
?>
30+
--EXPECT--
31+
TypeError
32+
TypeError
33+
ArgumentCountError
34+
OK

Zend/zend_partial.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,13 @@ static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *funct
10691069
return op_array;
10701070
}
10711071

1072+
static void zp_free_unbound_args(uint32_t start, uint32_t argc, zval *argv)
1073+
{
1074+
for (uint32_t offset = start; offset < argc; offset++) {
1075+
zval_ptr_dtor_nogc(&argv[offset]);
1076+
}
1077+
}
1078+
10721079
/* Bind pre-bound arguments as lexical vars */
10731080
static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *argv,
10741081
zend_array *extra_named_params) {
@@ -1102,6 +1109,7 @@ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *
11021109
zend_verify_arg_error(function, arg_info, offset+1, var);
11031110
zval_ptr_dtor(result);
11041111
ZVAL_NULL(result);
1112+
zp_free_unbound_args(offset, argc, argv);
11051113
return;
11061114
}
11071115
ZEND_ASSERT(zp_arg_must_be_sent_by_ref(function, offset+1) ? Z_ISREF_P(var) : !Z_ISREF_P(var));
@@ -1131,6 +1139,7 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
11311139

11321140
if (UNEXPECTED(!op_array)) {
11331141
ZEND_ASSERT(EG(exception));
1142+
zp_free_unbound_args(0, argc, argv);
11341143
ZVAL_NULL(result);
11351144
return;
11361145
}

0 commit comments

Comments
 (0)