Skip to content

Commit ec9a96d

Browse files
committed
Fixed bug #78335
Destroy static properties and variables prior to the final GC run, as they may hold GC roots.
1 parent 8dfd3af commit ec9a96d

File tree

3 files changed

+75
-48
lines changed

3 files changed

+75
-48
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ PHP NEWS
1111
(cmb)
1212
. Fixed bug #78454 (Consecutive numeric separators cause OOM error).
1313
(Theodore Brown)
14+
. Fixed bug #78335 (Static properties/variables containing cycles report as
15+
leak). (Nikita)
1416

1517
- FPM:
1618
. Fixed bug #78334 (fpm log prefix message includes wrong stdout/stderr

Zend/tests/bug78335.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Bug #78335: Static properties/variables containing cycles report as leak
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public static $test;
8+
9+
public static function method() {
10+
static $foo;
11+
$foo = [&$foo];
12+
}
13+
}
14+
15+
function test() {
16+
static $foo;
17+
$foo = [&$foo];
18+
}
19+
20+
$foo = [&$foo];
21+
Test::$test = $foo;
22+
test();
23+
Test::method();
24+
25+
?>
26+
===DONE===
27+
--EXPECT--
28+
===DONE===

Zend/zend_execute_API.c

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,54 @@ void shutdown_executor(void) /* {{{ */
270270
zend_close_rsrc_list(&EG(regular_list));
271271
} zend_end_try();
272272

273+
/* No PHP callback functions should be called after this point. */
274+
EG(active) = 0;
275+
273276
if (!fast_shutdown) {
274277
zend_hash_graceful_reverse_destroy(&EG(symbol_table));
275278

279+
/* Release static properties and static variables prior to the final GC run,
280+
* as they may hold GC roots. */
281+
ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) {
282+
zend_op_array *op_array = Z_PTR_P(zv);
283+
if (op_array->type == ZEND_INTERNAL_FUNCTION) {
284+
break;
285+
}
286+
if (op_array->static_variables) {
287+
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
288+
if (ht) {
289+
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
290+
zend_array_destroy(ht);
291+
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
292+
}
293+
}
294+
} ZEND_HASH_FOREACH_END();
295+
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
296+
zend_class_entry *ce = Z_PTR_P(zv);
297+
if (ce->type == ZEND_INTERNAL_CLASS) {
298+
break;
299+
}
300+
if (ce->default_static_members_count) {
301+
zend_cleanup_internal_class_data(ce);
302+
}
303+
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
304+
zend_op_array *op_array;
305+
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
306+
if (op_array->type == ZEND_USER_FUNCTION) {
307+
if (op_array->static_variables) {
308+
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
309+
if (ht) {
310+
if (GC_DELREF(ht) == 0) {
311+
zend_array_destroy(ht);
312+
}
313+
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
314+
}
315+
}
316+
}
317+
} ZEND_HASH_FOREACH_END();
318+
}
319+
} ZEND_HASH_FOREACH_END();
320+
276321
#if ZEND_DEBUG
277322
if (gc_enabled() && !CG(unclean_shutdown)) {
278323
gc_collect_cycles();
@@ -284,10 +329,6 @@ void shutdown_executor(void) /* {{{ */
284329

285330
zend_weakrefs_shutdown();
286331

287-
/* All resources and objects are destroyed. */
288-
/* No PHP callback functions may be called after this point. */
289-
EG(active) = 0;
290-
291332
zend_try {
292333
zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator);
293334
} zend_end_try();
@@ -348,57 +389,13 @@ void shutdown_executor(void) /* {{{ */
348389
zend_string_release_ex(key, 0);
349390
} ZEND_HASH_FOREACH_END_DEL();
350391

351-
/* Cleanup preloaded immutable functions */
352-
ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) {
353-
zend_op_array *op_array = Z_PTR_P(zv);
354-
if (op_array->type == ZEND_INTERNAL_FUNCTION) {
355-
break;
356-
}
357-
ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_IMMUTABLE);
358-
if (op_array->static_variables) {
359-
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
360-
if (ht) {
361-
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
362-
zend_array_destroy(ht);
363-
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
364-
}
365-
}
366-
} ZEND_HASH_FOREACH_END();
367-
368392
ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) {
369393
if (_idx == EG(persistent_classes_count)) {
370394
break;
371395
}
372396
destroy_zend_class(zv);
373397
zend_string_release_ex(key, 0);
374398
} ZEND_HASH_FOREACH_END_DEL();
375-
376-
/* Cleanup preloaded immutable classes */
377-
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
378-
zend_class_entry *ce = Z_PTR_P(zv);
379-
if (ce->type == ZEND_INTERNAL_CLASS) {
380-
break;
381-
}
382-
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_IMMUTABLE);
383-
if (ce->default_static_members_count) {
384-
zend_cleanup_internal_class_data(ce);
385-
}
386-
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
387-
zend_op_array *op_array;
388-
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
389-
if (op_array->type == ZEND_USER_FUNCTION) {
390-
if (op_array->static_variables) {
391-
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
392-
if (ht) {
393-
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
394-
zend_array_destroy(ht);
395-
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
396-
}
397-
}
398-
}
399-
} ZEND_HASH_FOREACH_END();
400-
}
401-
} ZEND_HASH_FOREACH_END();
402399
}
403400

404401
zend_cleanup_internal_classes();

0 commit comments

Comments
 (0)