]> granicus.if.org Git - php/commitdiff
Fixed bug #69534 (Cycle leaks through declared properties on internal classes)
authorDmitry Stogov <dmitry@zend.com>
Wed, 6 May 2015 18:33:49 +0000 (21:33 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 6 May 2015 18:33:49 +0000 (21:33 +0300)
Zend/tests/bug69534.phpt [new file with mode: 0644]
Zend/zend_gc.c
Zend/zend_object_handlers.c

diff --git a/Zend/tests/bug69534.phpt b/Zend/tests/bug69534.phpt
new file mode 100644 (file)
index 0000000..1f02754
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+Bug #69534 (Cycle leaks through declared properties on internal classes)
+--FILE--
+<?php
+class Node extends SplObjectStorage {
+    public $prop;
+}
+
+$node1 = new Node;
+$node2 = new Node;
+$node1->prop = $node2;
+$node2->prop = $node1;
+unset($node1);
+unset($node2);
+var_dump(gc_collect_cycles());
+?>
+--EXPECT--
+int(2)
index 30d75c2305bba2cd16752b99144b5b8e5fbc7e61..dc5a4580e9c41c880a01ba4c3124d7019477e8f3 100644 (file)
@@ -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;
        }
 }
index b85dd840e89e2ffef04b3da57b77550407439ab4..363ea139d6ecaa2d7ba7e5f54e06ecda1de1674b 100644 (file)
@@ -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;
+               }
        }
 }
 /* }}} */