summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2019-08-09 17:43:50 +0300
committerDmitry Stogov <dmitry@zend.com>2019-08-09 17:43:50 +0300
commit9b43e29d9b55eafb4e063a0b2d817356f5b908b5 (patch)
treebcde7c9bd1a5918a941828afcb5c477a9d9f268e
parent6b1cc1252e73e51e53194c8c65e3d2302bc83dca (diff)
downloadphp-git-9b43e29d9b55eafb4e063a0b2d817356f5b908b5.tar.gz
Fixed handling of references in nested data of objects with destructor
-rw-r--r--Zend/tests/gc_041.phpt35
-rw-r--r--Zend/zend_gc.c26
2 files changed, 49 insertions, 12 deletions
diff --git a/Zend/tests/gc_041.phpt b/Zend/tests/gc_041.phpt
new file mode 100644
index 0000000000..7400e23756
--- /dev/null
+++ b/Zend/tests/gc_041.phpt
@@ -0,0 +1,35 @@
+--TEST--
+GC 041: Handling of references in nested data of objects with destructor
+--INI--
+zend.enable_gc = 1
+--FILE--
+<?php
+class ryat {
+ var $ryat;
+ var $chtg;
+ var $nested;
+ function __destruct() {
+ $GLOBALS['x'] = $this;
+ }
+}
+$o = new ryat;
+$o->nested = [];
+$o->nested[] =& $o->nested;
+$o->ryat = $o;
+$x =& $o->chtg;
+unset($o);
+gc_collect_cycles();
+var_dump($x);
+?>
+--EXPECT--
+object(ryat)#1 (3) {
+ ["ryat"]=>
+ *RECURSION*
+ ["chtg"]=>
+ *RECURSION*
+ ["nested"]=>
+ &array(1) {
+ [0]=>
+ *RECURSION*
+ }
+} \ No newline at end of file
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index 55f7271e3f..d98598cc48 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -953,11 +953,9 @@ static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buff
zval *zv;
tail_call:
- if (root ||
- (GC_ADDRESS(GC_INFO(ref)) != 0 &&
- GC_REF_GET_COLOR(ref) == GC_BLACK)) {
- GC_TRACE_REF(ref, "removing from buffer");
+ do {
if (root) {
+ GC_TRACE_REF(ref, "removing from buffer");
if (EXPECTED(GC_ADDRESS(GC_INFO(root->ref)) < GC_ROOT_BUFFER_MAX_ENTRIES)) {
gc_remove_from_roots(root);
} else {
@@ -965,8 +963,18 @@ tail_call:
}
GC_INFO(ref) = 0;
root = NULL;
- } else {
+ } else if (GC_ADDRESS(GC_INFO(ref)) != 0
+ && GC_REF_GET_COLOR(ref) == GC_BLACK) {
+ GC_TRACE_REF(ref, "removing from buffer");
GC_REMOVE_FROM_BUFFER(ref);
+ } else if (GC_TYPE(ref) == IS_REFERENCE) {
+ if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
+ ref = Z_COUNTED(((zend_reference*)ref)->val);
+ goto tail_call;
+ }
+ return;
+ } else {
+ return;
}
if (GC_TYPE(ref) == IS_OBJECT) {
@@ -1004,12 +1012,6 @@ tail_call:
}
} else if (GC_TYPE(ref) == IS_ARRAY) {
ht = (zend_array*)ref;
- } else if (GC_TYPE(ref) == IS_REFERENCE) {
- if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
- ref = Z_COUNTED(((zend_reference*)ref)->val);
- goto tail_call;
- }
- return;
} else {
return;
}
@@ -1045,7 +1047,7 @@ tail_call:
}
ref = Z_COUNTED_P(zv);
goto tail_call;
- }
+ } while (0);
}
ZEND_API int zend_gc_collect_cycles(void)