Skip to content

Commit e3ff9f1

Browse files
authored
Fix memory leak in array_map optimization (#24138)
1 parent bb9b4a4 commit e3ff9f1

3 files changed

Lines changed: 50 additions & 9 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ PHP NEWS
134134
CLOCK_MONOTONIC_RAW. (nicolas-grekas)
135135
. Fixed array_map optimization with non-literal function or non-literal args.
136136
(Arnaud)
137+
. Fixed memory leak in array_map() foreach optimization when a constant
138+
non-array string argument is passed. (Alexander Danilov)
137139
. Fixed use-after-free when a dl()-loaded extension declares a frameless
138140
function and a later request calls it. (Ilia Alshanetsky)
139141
. Fixed System V shared memory emulation for Windows attaching segments past
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
array_map(): foreach optimization - constant non-array argument does not leak
3+
--FILE--
4+
<?php
5+
6+
$tests = [
7+
'string' => fn() => array_map(strtoupper(...), "str"),
8+
'int' => fn() => array_map(strtoupper(...), 1),
9+
'float' => fn() => array_map(strtoupper(...), 1.5),
10+
'true' => fn() => array_map(strtoupper(...), true),
11+
'null' => fn() => array_map(strtoupper(...), null),
12+
'folded string' => fn() => array_map(strtoupper(...), "a" . "b"),
13+
'static call' => fn() => array_map(Foo::bar(...), "str"),
14+
];
15+
16+
foreach ($tests as $name => $test) {
17+
try {
18+
$test();
19+
} catch (Throwable $e) {
20+
echo $name, ': ', get_class($e), ': ', $e->getMessage(), "\n";
21+
}
22+
}
23+
24+
?>
25+
--EXPECT--
26+
string: TypeError: array_map(): Argument #2 ($array) must be of type array, string given
27+
int: TypeError: array_map(): Argument #2 ($array) must be of type array, int given
28+
float: TypeError: array_map(): Argument #2 ($array) must be of type array, float given
29+
true: TypeError: array_map(): Argument #2 ($array) must be of type array, true given
30+
null: TypeError: array_map(): Argument #2 ($array) must be of type array, null given
31+
folded string: TypeError: array_map(): Argument #2 ($array) must be of type array, string given
32+
static call: Error: Class "Foo" not found

‎Zend/zend_compile.c‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5415,15 +5415,7 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
54155415
zend_ast *call_args = zend_partial_apply(callback,
54165416
zend_ast_create_znode(&value));
54175417
if (!call_args) {
5418-
CG(active_op_array)->T--;
5419-
if (func_node.op_type == IS_CONST) {
5420-
zval_ptr_dtor_nogc(&func_node.u.constant);
5421-
}
5422-
if (class_node.op_type == IS_CONST) {
5423-
zval_ptr_dtor_nogc(&class_node.u.constant);
5424-
}
5425-
/* The callback is not a FCC/PFA, or is not optimizable */
5426-
return FAILURE;
5418+
goto fail;
54275419
}
54285420

54295421
zend_op *opline;
@@ -5432,6 +5424,10 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
54325424
zend_compile_expr(&array, args->child[1]);
54335425
/* array is an argument to both ZEND_TYPE_ASSERT and to ZEND_FE_RESET_R. */
54345426
if (array.op_type == IS_CONST) {
5427+
if (Z_TYPE(array.u.constant) != IS_ARRAY) {
5428+
zval_ptr_dtor_nogc(&array.u.constant);
5429+
goto fail;
5430+
}
54355431
Z_TRY_ADDREF(array.u.constant);
54365432
}
54375433

@@ -5512,6 +5508,17 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
55125508
}
55135509

55145510
return SUCCESS;
5511+
5512+
fail:
5513+
CG(active_op_array)->T--;
5514+
if (func_node.op_type == IS_CONST) {
5515+
zval_ptr_dtor_nogc(&func_node.u.constant);
5516+
}
5517+
if (class_node.op_type == IS_CONST) {
5518+
zval_ptr_dtor_nogc(&class_node.u.constant);
5519+
}
5520+
5521+
return FAILURE;
55155522
}
55165523

55175524
static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, uint32_t type, uint32_t lineno) /* {{{ */

0 commit comments

Comments
 (0)