com php-src: Bug Fix: Corrupted class entries on shutdown when a destructor spawns another object (C) 2017 CommerceByte Consulting:
Zend/zend_objects_API.c Zend/zend_objects_API.h
| From: | Dmitry Stogov | Date: | Mon, 06 Feb 2017 23:05:34 +0000 |
| Subject: | com php-src: Bug Fix: Corrupted class entries on shutdown when a destructor spawns another object (C) 2017 CommerceByte Consulting: Zend/zend_objects_API.c Zend/zend_objects_API.h |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-97055@lists.php.net to get a copy of this message | ||
Commit: 9a8a1c9da129a72c9bf8f3392f954a9e5ca317ff
Author: Jim Zubov <jim@commercebyte.com> Mon, 6 Feb 2017 18:05:34 -0500
Parents: 31332d094127b6332018cd146b9becbd7566e60c
Branches: master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=9a8a1c9da129a72c9bf8f3392f954a9e5ca317ff
Log:
Bug Fix:
Corrupted class entries on shutdown when a destructor spawns another object
(C) 2017 CommerceByte Consulting
When zend_objects_store_call_destructors() is called from the shutdown sequence -
it's calling the dtor's for remaining objects one by one in sequence of object handles.
If the dtor spawns one or more objects, and the new objects happen to reuse the old handles -
their dtor's are not called in this cycle.
The dtor's are called later on, when zend_deactivete() kicks in, and the static property lists
in the class entries are freed.
This causes "Undefined static property" errors, and/or SIGSEGV.
Solution:
zend_object_store.no_reuse field is added
Set to 0 on initialization, set to 1 on the shutdown sequence.
zend_objects_store_put(zend_object *) checks the no_reuse flag, and never reuses the old handle
slots if set.
This way, the dtor's for newly spawned objects are guaranteed to be called in the
zend_objects_store_call_destructors() loop.
Changed paths:
M Zend/zend_objects_API.c
M Zend/zend_objects_API.h
Diff:
diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c
index 858113a..a0de331 100644
--- a/Zend/zend_objects_API.c
+++ b/Zend/zend_objects_API.c
@@ -32,6 +32,7 @@ ZEND_API void zend_objects_store_init(zend_objects_store *objects, uint32_t init
objects->top = 1; /* Skip 0 so that handles are true */
objects->size = init_size;
objects->free_list_head = -1;
+ objects->no_reuse = 0;
memset(&objects->object_buckets[0], 0, sizeof(zend_object*));
}
@@ -43,6 +44,7 @@ ZEND_API void zend_objects_store_destroy(zend_objects_store *objects)
ZEND_API void zend_objects_store_call_destructors(zend_objects_store *objects)
{
+ objects->no_reuse = 1; /* new objects spawned by dtors will never reuse unused slots, so their
own dtors will be called further down the loop */
if (objects->top > 1) {
uint32_t i;
for (i = 1; i < objects->top; i++) {
@@ -111,7 +113,7 @@ ZEND_API void zend_objects_store_put(zend_object *object)
{
int handle;
- if (EG(objects_store).free_list_head != -1) {
+ if (!EG(objects_store).no_reuse && EG(objects_store).free_list_head != -1) {
handle = EG(objects_store).free_list_head;
EG(objects_store).free_list_head =
GET_OBJ_BUCKET_NUMBER(EG(objects_store).object_buckets[handle]);
} else {
diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h
index 2230600..ebf0487 100644
--- a/Zend/zend_objects_API.h
+++ b/Zend/zend_objects_API.h
@@ -45,6 +45,7 @@ typedef struct _zend_objects_store {
uint32_t top;
uint32_t size;
int free_list_head;
+ char no_reuse; /* to be set to true when shutting down, to avoid missing dtor call on objects
spawned by another dtor */
} zend_objects_store;
/* Global store handling functions */