Skip to content

Commit cfe3a0f

Browse files
committed
issue #66085 experimental fix
1 parent cca75c6 commit cfe3a0f

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

ext/standard/basic_functions.h

+6
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ typedef signed long php_int32;
161161

162162
#define MT_N (624)
163163

164+
typedef struct _zval_chain {
165+
zval* value;
166+
struct _zval_chain* next;
167+
} zval_chain;
168+
164169
typedef struct _php_basic_globals {
165170
HashTable *user_shutdown_function_names;
166171
HashTable putenv_ht;
@@ -208,6 +213,7 @@ typedef struct _php_basic_globals {
208213
struct {
209214
void *var_hash;
210215
unsigned level;
216+
zval_chain* zval_refs;
211217
} serialize;
212218
struct {
213219
void *var_hash;

ext/standard/php_var.h

+3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ typedef struct php_unserialize_data* php_unserialize_data_t;
5151

5252
PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC);
5353
PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC);
54+
void php_var_serialize_release_zval_chain(zval_chain* zval_refs);
5455

5556
#define PHP_VAR_SERIALIZE_INIT(var_hash_ptr) \
5657
do { \
@@ -61,6 +62,7 @@ do { \
6162
if (!BG(serialize_lock)) { \
6263
BG(serialize).var_hash = (void *)(var_hash_ptr); \
6364
BG(serialize).level = 1; \
65+
BG(serialize).zval_refs = NULL; \
6466
} \
6567
} else { \
6668
(var_hash_ptr) = (php_serialize_data_t)BG(serialize).var_hash; \
@@ -79,6 +81,7 @@ do { \
7981
zend_hash_destroy((php_serialize_data_t)BG(serialize).var_hash); \
8082
FREE_HASHTABLE((php_serialize_data_t)BG(serialize).var_hash); \
8183
BG(serialize).var_hash = NULL; \
84+
php_var_serialize_release_zval_chain(BG(serialize).zval_refs); \
8285
} \
8386
} \
8487
} while (0)

ext/standard/var.c

+58
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,61 @@ PHP_FUNCTION(var_export)
543543

544544
static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var_hash TSRMLS_DC);
545545

546+
/**
547+
* Iterates over zvals captured during serialization, and releases/decrements them
548+
*/
549+
void php_var_serialize_release_zval_chain(zval_chain* zval_refs) {
550+
zval_chain* node = BG(serialize).zval_refs;
551+
zval_chain* next = NULL;
552+
int count = 0;
553+
554+
while (node != NULL) {
555+
#if 0
556+
fprintf(stderr, "- zval chain %d: ref (%d) %p\n", count, Z_REFCOUNT_P(node->value), node->value);
557+
#endif
558+
count++;
559+
next = node->next;
560+
zval_ptr_dtor(&(node->value));
561+
//Z_DELREF_P(node->value);
562+
efree(node);
563+
node = next;
564+
}
565+
}
566+
567+
/**
568+
* Increments zval reference count in order to keep it alive for the duration of object graph serialization;
569+
* registers zval in threadlocal list of zvals to decrement/release when topmost serialization frame is popped
570+
*/
571+
static inline void php_var_serialize_capture_zval(zval* var) {
572+
// increment reference count
573+
zend_uint refcount = Z_ADDREF_P(var);
574+
//zval_copy_ctor(var);
575+
576+
// store on threadlocal serialization list for decrement when serialization frame is popped
577+
zval_chain* new_zval = emalloc(sizeof(zval_chain));
578+
new_zval->value = var;
579+
new_zval->next = NULL;
580+
zval_chain** zval_refs = &BG(serialize).zval_refs;
581+
582+
// optimize later:
583+
// new_zval->next = *zval_refs;
584+
// *zval_refs = new_zval;
585+
586+
// singly linked list - find the end and append
587+
int i = 0;
588+
while (*zval_refs) {
589+
zval_refs = &((**zval_refs).next);
590+
i++;
591+
}
592+
*zval_refs = new_zval;
593+
594+
#if 0
595+
fprintf(stderr, "+ zval chain %d: ref (%d) %p\n", i, Z_REFCOUNT_P(var), var);
596+
#endif
597+
598+
}
599+
600+
546601
static inline int php_add_var_hash(HashTable *var_hash, zval *var, void *var_old TSRMLS_DC) /* {{{ */
547602
{
548603
ulong var_no;
@@ -572,6 +627,9 @@ static inline int php_add_var_hash(HashTable *var_hash, zval *var, void *var_old
572627
return FAILURE;
573628
}
574629

630+
// capture the zval to keep it alive for the duration of serialization
631+
php_var_serialize_capture_zval(var);
632+
575633
/* +1 because otherwise hash will think we are trying to store NULL pointer */
576634
var_no = zend_hash_num_elements(var_hash) + 1;
577635
zend_hash_add(var_hash, p, len, &var_no, sizeof(var_no), NULL);

0 commit comments

Comments
 (0)