diff --git a/Zend/tests/bug70805.phpt b/Zend/tests/bug70805.phpt new file mode 100644 index 00000000000..256f52eed52 --- /dev/null +++ b/Zend/tests/bug70805.phpt @@ -0,0 +1,46 @@ +--TEST-- +Bug #70805 (Segmentation faults whilst running Drupal 8 test suite) +--FILE-- +b = new B; +$a->b->a = $a; + +$i = 0; + +$c = new A; +$array = array($c); //This is used to leave a room for $GLOBALS["a"] +unset($c); + +while ($i++ < 9997) { + $t = []; + $t[] = &$t; + unset($t); +} +$t = [new C]; +$t[] = &$t; +unset($t); // This is used to trigger C::__destruct while doing gc_colloct_roots + +$e = $a; +unset($a); // This one can not be putted into roots buf because it's full, thus gc_colloct_roots will be called, + // but C::__destructor which is called in gc_colloct_roots will put $a into buf + // which will make $a be putted into gc roots buf twice +var_dump(gc_collect_cycles()); +?> +--EXPECT-- +int(0) diff --git a/Zend/tests/bug70805_1.phpt b/Zend/tests/bug70805_1.phpt new file mode 100644 index 00000000000..0225b4ce82b --- /dev/null +++ b/Zend/tests/bug70805_1.phpt @@ -0,0 +1,43 @@ +--TEST-- +Bug #70805 (Segmentation faults whilst running Drupal 8 test suite) (Crash) +--FILE-- +b = new B; +$a->b->a = $a; + +$i = 0; + +$c = new A; +$array = array($c); +unset($c); + +while ($i++ < 9997) { + $t = []; + $t[] = &$t; + unset($t); +} +$t = [new C]; +$t[] = &$t; +unset($t); +unset($a); + +var_dump(gc_collect_cycles()); +?> +--EXPECT-- +int(2) diff --git a/Zend/tests/bug70805_2.phpt b/Zend/tests/bug70805_2.phpt new file mode 100644 index 00000000000..a9b11684f51 --- /dev/null +++ b/Zend/tests/bug70805_2.phpt @@ -0,0 +1,37 @@ +--TEST-- +Bug #70805 (Segmentation faults whilst running Drupal 8 test suite) (Memleak) +--FILE-- +b = new B; +$a->b->a = $a; + +$i = 0; + +while ($i++ < 9998) { + $t = []; + $t[] = &$t; + unset($t); +} +$t = [new C]; +$t[] = &$t; +unset($t); + +unset($a); +var_dump(gc_collect_cycles()); +--EXPECT-- +int(2) diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index e223a499068..74c534844d7 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -242,6 +242,13 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref) GC_REFCOUNT(ref)++; gc_collect_cycles(); GC_REFCOUNT(ref)--; + if (UNEXPECTED(GC_REFCOUNT(ref)) == 0) { + zval_dtor_func_for_ptr(ref); + return; + } + if (UNEXPECTED(GC_INFO(ref))) { + return; + } newRoot = GC_G(unused); if (!newRoot) { #if ZEND_GC_DEBUG diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 8a8ef2948ea..52868cf4a6d 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1049,9 +1049,13 @@ ZEND_API int ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key) return FAILURE; } else { if (ht->pDestructor) { - ht->pDestructor(data); + zval tmp; + ZVAL_COPY_VALUE(&tmp, data); + ZVAL_UNDEF(data); + ht->pDestructor(&tmp); + } else { + ZVAL_UNDEF(data); } - ZVAL_UNDEF(data); } } else { _zend_hash_del_el_ex(ht, idx, p, prev); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 561d79354b7..5cee5bf695b 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5605,8 +5605,14 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED, VAR_FETCH) ZVAL_UNDEF(var); zval_dtor_func_for_ptr(garbage); } else { - GC_ZVAL_CHECK_POSSIBLE_ROOT(var); - ZVAL_UNDEF(var); + zval *z = var; + ZVAL_DEREF(z); + if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) { + ZVAL_UNDEF(var); + gc_possible_root(Z_COUNTED_P(z)); + } else { + ZVAL_UNDEF(var); + } } } else { ZVAL_UNDEF(var); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 34f5fbe90e8..813879f0a15 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7737,8 +7737,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HA ZVAL_UNDEF(var); zval_dtor_func_for_ptr(garbage); } else { - GC_ZVAL_CHECK_POSSIBLE_ROOT(var); - ZVAL_UNDEF(var); + zval *z = var; + ZVAL_DEREF(z); + if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) { + ZVAL_UNDEF(var); + gc_possible_root(Z_COUNTED_P(z)); + } else { + ZVAL_UNDEF(var); + } } } else { ZVAL_UNDEF(var); @@ -35008,8 +35014,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDL ZVAL_UNDEF(var); zval_dtor_func_for_ptr(garbage); } else { - GC_ZVAL_CHECK_POSSIBLE_ROOT(var); - ZVAL_UNDEF(var); + zval *z = var; + ZVAL_DEREF(z); + if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) { + ZVAL_UNDEF(var); + gc_possible_root(Z_COUNTED_P(z)); + } else { + ZVAL_UNDEF(var); + } } } else { ZVAL_UNDEF(var); @@ -43180,8 +43192,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_H ZVAL_UNDEF(var); zval_dtor_func_for_ptr(garbage); } else { - GC_ZVAL_CHECK_POSSIBLE_ROOT(var); - ZVAL_UNDEF(var); + zval *z = var; + ZVAL_DEREF(z); + if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) { + ZVAL_UNDEF(var); + gc_possible_root(Z_COUNTED_P(z)); + } else { + ZVAL_UNDEF(var); + } } } else { ZVAL_UNDEF(var);