Fixed bug #72286 (Segmentation fault During Garbage Collection)
authorDmitry Stogov <dmitry@zend.com>
Thu, 14 Jul 2016 19:37:25 +0000 (22:37 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 14 Jul 2016 19:37:25 +0000 (22:37 +0300)
NEWS
Zend/tests/bug72286.phpt [new file with mode: 0644]
Zend/zend_gc.c

diff --git a/NEWS b/NEWS
index a9d242b1dc2f25f6c848066fc21eed777ca03584..a9ca67ad8c4d64720b2d77a458bfc4e4b7f6f5c9 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ PHP                                                                        NEWS
 - Core:
   . Fixed bug #72581 (previous property undefined in Exception after
     deserialization). (Laruence)
+  . Fixed bug #72286 (Segmentation fault During Garbage Collection). (Dmitry)
   . Fixed bug #72024 (microtime() leaks memory). (maroszek at gmx dot net)
 
 - Curl:
diff --git a/Zend/tests/bug72286.phpt b/Zend/tests/bug72286.phpt
new file mode 100644 (file)
index 0000000..0f6184d
--- /dev/null
@@ -0,0 +1,51 @@
+--TEST--
+Bug #72286 (Segmentation fault During Garbage Collection)
+--FILE--
+<?php
+class SegfaultScenario
+{
+    private $circular_reference;
+    private $object;
+
+    public function __construct()
+    {
+        $this->circular_reference = $this;
+        $this->object = new \stdClass();
+    }
+
+    public function __destruct()
+    {
+        // we try to avoid accessing $this->object by returning early but the object exists
+        if (!$this->object) { // without this expression involving the object, the segfault does not happen
+            var_dump('the object exists');
+            return;
+        }
+
+        var_dump('segfaults here:');
+        // and then access the object (which seemingly has already been garbage collected)
+        var_dump($this->object);
+        var_dump('will not get here');
+    }
+}
+
+class SomeContainer
+{
+    public function run()
+    {
+        new SegfaultScenario();
+    }
+}
+
+$container = new SomeContainer();
+$container->run();
+
+var_dump('we are about to segfault');
+gc_collect_cycles();
+var_dump('will not get here');
+--EXPECTF--
+string(24) "we are about to segfault"
+string(15) "segfaults here:"
+object(stdClass)#%d (0) {
+}
+string(17) "will not get here"
+string(17) "will not get here"
index e72655c71ce039fb89cd641dd24727fd0d7ea017..7ad734019b2158ad947d07a41f08c6c18b2877df 100644 (file)
@@ -650,6 +650,7 @@ tail_call:
 
                                        if (!props) {
                                                /* restore refcount and put into list to free */
+                                               obj->refcount = 1;
                                                pz->refcount__gc++;
                                                ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
                                                GC_G(zval_to_free) = (zval_gc_info*)pz;
@@ -756,6 +757,7 @@ static void gc_collect_roots(TSRMLS_D)
                                struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
                                zval z;
 
+                               obj->refcount = 1;
                                GC_SET_ADDRESS(obj->buffered, NULL);
                                INIT_PZVAL(&z);
                                Z_OBJ_HANDLE(z) = current->handle;
@@ -802,7 +804,7 @@ ZEND_API int gc_collect_cycles(TSRMLS_D)
                        if (Z_TYPE(p->z) == IS_OBJECT) {
                                if (EG(objects_store).object_buckets &&
                                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
-                                       EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
+                                       EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 1 &&
                                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
                                        !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
 
@@ -823,7 +825,7 @@ ZEND_API int gc_collect_cycles(TSRMLS_D)
                        if (Z_TYPE(p->z) == IS_OBJECT) {
                                if (EG(objects_store).object_buckets &&
                                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
-                                       EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
+                                       --EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
                                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
                                        Z_TYPE(p->z) = IS_NULL;
                                        zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);