From 2a9f9860d724296e4853515c04dea3dcb729654d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 6 May 2015 21:33:49 +0300 Subject: [PATCH] Fixed bug #69534 (Cycle leaks through declared properties on internal classes) --- Zend/tests/bug69534.phpt | 18 +++++ Zend/zend_gc.c | 141 +++++++++++++++++++++++++++++------- Zend/zend_object_handlers.c | 12 ++- 3 files changed, 140 insertions(+), 31 deletions(-) create mode 100644 Zend/tests/bug69534.phpt diff --git a/Zend/tests/bug69534.phpt b/Zend/tests/bug69534.phpt new file mode 100644 index 0000000000..1f0275485e --- /dev/null +++ b/Zend/tests/bug69534.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #69534 (Cycle leaks through declared properties on internal classes) +--FILE-- +prop = $node2; +$node2->prop = $node1; +unset($node1); +unset($node2); +var_dump(gc_collect_cycles()); +?> +--EXPECT-- +int(2) diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index 30d75c2305..dc5a4580e9 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -295,6 +295,7 @@ static void gc_scan_black(zend_refcounted *ref) { HashTable *ht; Bucket *p, *end; + zval *zv; tail_call: ht = NULL; @@ -362,12 +363,24 @@ tail_call: if (!ht->nNumUsed) return; p = ht->arData; end = p + ht->nNumUsed; - while (!Z_REFCOUNTED((--end)->val)) { + while (1) { + end--; + zv = &end->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + break; + } if (p == end) return; } while (p != end) { - if (Z_REFCOUNTED(p->val)) { - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)++; if (GC_REF_GET_COLOR(ref) != GC_BLACK) { gc_scan_black(ref); @@ -375,7 +388,11 @@ tail_call: } p++; } - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)++; if (GC_REF_GET_COLOR(ref) != GC_BLACK) { goto tail_call; @@ -386,6 +403,7 @@ static void gc_mark_grey(zend_refcounted *ref) { HashTable *ht; Bucket *p, *end; + zval *zv; tail_call: if (GC_REF_GET_COLOR(ref) != GC_GREY) { @@ -454,27 +472,43 @@ tail_call: if (!ht->nNumUsed) return; p = ht->arData; end = p + ht->nNumUsed; - while (!Z_REFCOUNTED((--end)->val)) { + while (1) { + end--; + zv = &end->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + break; + } if (p == end) return; } while (p != end) { - if (Z_REFCOUNTED(p->val)) { - if (Z_TYPE(p->val) == IS_OBJECT && + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + if (Z_TYPE_P(zv) == IS_OBJECT && UNEXPECTED(!EG(objects_store).object_buckets)) { - Z_TYPE_INFO(p->val) = IS_NULL; + Z_TYPE_INFO_P(zv) = IS_NULL; } else { - ref = Z_COUNTED(p->val); + ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)--; gc_mark_grey(ref); } } p++; } - if (Z_TYPE(p->val) == IS_OBJECT && + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_TYPE_P(zv) == IS_OBJECT && UNEXPECTED(!EG(objects_store).object_buckets)) { - Z_TYPE_INFO(p->val) = IS_NULL; + Z_TYPE_INFO_P(zv) = IS_NULL; } else { - ref = Z_COUNTED(p->val); + ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)--; goto tail_call; } @@ -497,6 +531,7 @@ static void gc_scan(zend_refcounted *ref) { HashTable *ht; Bucket *p, *end; + zval *zv; tail_call: if (GC_REF_GET_COLOR(ref) == GC_GREY) { @@ -557,17 +592,33 @@ tail_call: if (!ht->nNumUsed) return; p = ht->arData; end = p + ht->nNumUsed; - while (!Z_REFCOUNTED((--end)->val)) { + while (1) { + end--; + zv = &end->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + break; + } if (p == end) return; } while (p != end) { - if (Z_REFCOUNTED(p->val)) { - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + ref = Z_COUNTED_P(zv); gc_scan(ref); } p++; } - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + ref = Z_COUNTED_P(zv); goto tail_call; } } @@ -641,6 +692,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_additional int count = 0; HashTable *ht; Bucket *p, *end; + zval *zv; tail_call: if (GC_REF_GET_COLOR(ref) == GC_WHITE) { @@ -731,25 +783,41 @@ tail_call: if (!ht->nNumUsed) return count; p = ht->arData; end = p + ht->nNumUsed; - while (!Z_REFCOUNTED((--end)->val)) { + while (1) { + end--; + zv = &end->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + break; + } /* count non-refcounted for compatibility ??? */ - if (Z_TYPE(end->val) != IS_UNDEF && Z_TYPE(end->val) != IS_INDIRECT) { + if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } - if (p == end) return count; + if (p == end) return; } while (p != end) { - if (Z_REFCOUNTED(p->val)) { - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)++; count += gc_collect_white(ref, flags, additional_buffer); /* count non-refcounted for compatibility ??? */ - } else if (Z_TYPE(p->val) != IS_UNDEF && Z_TYPE(p->val) != IS_INDIRECT) { - count++; + } else if (Z_TYPE_P(zv) != IS_UNDEF) { + count++; } p++; } - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)++; goto tail_call; } @@ -806,6 +874,7 @@ static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buff { HashTable *ht = NULL; Bucket *p, *end; + zval *zv; tail_call: if (root || @@ -870,17 +939,33 @@ tail_call: if (!ht->nNumUsed) return; p = ht->arData; end = p + ht->nNumUsed; - while (!Z_REFCOUNTED((--end)->val)) { + while (1) { + end--; + zv = &end->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + break; + } if (p == end) return; } while (p != end) { - if (Z_REFCOUNTED(p->val)) { - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + if (Z_REFCOUNTED_P(zv)) { + ref = Z_COUNTED_P(zv); gc_remove_nested_data_from_buffer(ref, NULL); } p++; } - ref = Z_COUNTED(p->val); + zv = &p->val; + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + ref = Z_COUNTED_P(zv); goto tail_call; } } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index b85dd840e8..363ea139d6 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -127,9 +127,15 @@ ZEND_API HashTable *zend_std_get_gc(zval *object, zval **table, int *n) /* {{{ * } else { zend_object *zobj = Z_OBJ_P(object); - *table = zobj->properties_table; - *n = zobj->ce->default_properties_count; - return zobj->properties; + if (zobj->properties) { + *table = NULL; + *n = 0; + return zobj->properties; + } else { + *table = zobj->properties_table; + *n = zobj->ce->default_properties_count; + return NULL; + } } } /* }}} */ -- 2.40.0