- 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:
--- /dev/null
+--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"
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;
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;
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) {
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);