Skip to content

Commit c191020

Browse files
authored
Fix double-free of closure run_time_cache in partial application (#22790)
A partial over a closure whose op_array is not persistent shallow-copies the closure's op_array into the per-request PFA cache as a lifetime anchor. The copy inherited ZEND_ACC_HEAP_RT_CACHE and the run_time_cache pointer, so destroy_op_array() freed the closure's cache twice: once when the closure was released and again at PFA cache teardown. Use function_add_ref() for the copy, which resets the per-request run_time_cache and static_variables_ptr map pointers, so only the live closure owns and frees the cache.
1 parent 74145e9 commit c191020

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Partial application over a Closure::fromCallable() must not double-free the run_time_cache
3+
--FILE--
4+
<?php
5+
class C {
6+
public function m($x, $y) { return $x + $y; }
7+
}
8+
9+
$cl = Closure::fromCallable([new C, 'm']);
10+
$partial = $cl(10, ?);
11+
var_dump($partial(5));
12+
unset($partial);
13+
unset($cl);
14+
gc_collect_cycles();
15+
echo "OK\n";
16+
?>
17+
--EXPECT--
18+
int(15)
19+
OK

ext/opcache/ZendAccelerator.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2146,8 +2146,7 @@ zend_op_array *zend_accel_compile_pfa(zend_ast *ast,
21462146
* See comment in zend_accel_pfa_key(). */
21472147
zend_op_array *copy = zend_arena_alloc(&CG(arena), sizeof(*copy));
21482148
memcpy(copy, called_function, sizeof(*copy));
2149-
zend_string_addref(copy->function_name);
2150-
(*copy->refcount)++;
2149+
function_add_ref((zend_function *) copy);
21512150
/* Reference the copy in op_array->dynamic_func_defs so that it's
21522151
* destroyed when op_array is destroyed. */
21532152
ZEND_ASSERT(!op_array->dynamic_func_defs && !op_array->num_dynamic_func_defs);

0 commit comments

Comments
 (0)