From 94e9d0a2ae76bad712495d820d3962e401085fef Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Sat, 17 Feb 2018 16:31:59 +0800 Subject: [PATCH] Fixed bug #75961 (Strange references behavior) --- NEWS | 1 + ext/standard/array.c | 30 ++++++++++++++++---------- ext/standard/tests/array/bug75961.phpt | 18 ++++++++++++++++ 3 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 ext/standard/tests/array/bug75961.phpt diff --git a/NEWS b/NEWS index 6e7e7e8840..ea0ecc8638 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,7 @@ PHP NEWS . Fixed bug #74519 (strange behavior of AppendIterator). (jhdxr) - Standard: + . Fixed bug #75961 (Strange references behavior). (Laruence) . Fixed bug #75916 (DNS_CAA record results contain garbage). (Mike, Philip Sharp) diff --git a/ext/standard/array.c b/ext/standard/array.c index 07db375440..d7fa94e52c 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1380,6 +1380,7 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */ /* Iterate through hash */ do { + zend_bool was_ref; /* Retrieve value */ zv = zend_hash_get_current_data_ex(target_hash, &pos); if (zv == NULL) { @@ -1395,6 +1396,8 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */ } } + was_ref = Z_ISREF_P(zv); + /* Ensure the value is a reference. Otherwise the location of the value may be freed. */ ZVAL_MAKE_REF(zv); @@ -1412,14 +1415,16 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */ HashTable *thash; zend_fcall_info orig_array_walk_fci; zend_fcall_info_cache orig_array_walk_fci_cache; - zval ref; - ZVAL_COPY_VALUE(&ref, zv); + zval rv; - ZVAL_DEREF(zv); - SEPARATE_ARRAY(zv); - thash = Z_ARRVAL_P(zv); + SEPARATE_ARRAY(Z_REFVAL_P(zv)); + ZVAL_COPY_VALUE(&rv, Z_REFVAL_P(zv)); + thash = Z_ARRVAL(rv); if (thash->u.v.nApplyCount > 1) { php_error_docref(NULL, E_WARNING, "recursion detected"); + if (!was_ref) { + ZVAL_UNREF(zv); + } result = FAILURE; break; } @@ -1428,15 +1433,15 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */ orig_array_walk_fci = BG(array_walk_fci); orig_array_walk_fci_cache = BG(array_walk_fci_cache); - Z_ADDREF(ref); + Z_ADDREF(rv); thash->u.v.nApplyCount++; - result = php_array_walk(zv, userdata, recursive); - if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) { + result = php_array_walk(&rv, userdata, recursive); + if (Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL_P(zv))) { /* If the hashtable changed in the meantime, we'll "leak" this apply count * increment -- our reference to thash is no longer valid. */ thash->u.v.nApplyCount--; } - zval_ptr_dtor(&ref); + zval_ptr_dtor(&rv); /* restore the fcall info and cache */ BG(array_walk_fci) = orig_array_walk_fci; @@ -1452,12 +1457,15 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */ zval_ptr_dtor(&args[0]); } - if (Z_TYPE(args[1]) != IS_UNDEF) { zval_ptr_dtor(&args[1]); ZVAL_UNDEF(&args[1]); } - + if (!was_ref && Z_ISREF_P(zv)) { + if (Z_REFCOUNT_P(zv) == 1) { + ZVAL_UNREF(zv); + } + } if (result == FAILURE) { break; } diff --git a/ext/standard/tests/array/bug75961.phpt b/ext/standard/tests/array/bug75961.phpt new file mode 100644 index 0000000000..15484ab16f --- /dev/null +++ b/ext/standard/tests/array/bug75961.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #75961 (Strange references behavior) +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + array(1) { + [0]=> + int(1) + } +} -- 2.49.0