From b175ea42154420f470102a98758eb6aa50fbb66d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 29 Nov 2023 20:35:13 +0100 Subject: [PATCH] Fix GH-12826: Weird pointers issue in nested loops This regressed in cd53ce838a. The loop with `zend_hash_iterators_update` hangs forever because `iter_pos` can't advance to idx. This is because the `zend_hash_iterators_lower_pos` upper bound is `target->nNumUsed`, but that is set to `source->nNumOfElements`. That means that if there are holes in the array, we still loop over all the buckets but the number of bucket slots will not match. Fix it by changing the assignment. Closes GH-12831. --- NEWS | 1 + Zend/tests/gh12826.phpt | 30 ++++++++++++++++++++++++++++++ Zend/zend_hash.c | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/gh12826.phpt diff --git a/NEWS b/NEWS index 45610bb0ccf76..8037fb7354f71 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PHP NEWS . Fix various missing NULL checks. (nielsdos, dstogov) . Fixed bug GH-12835 (Leak of call->extra_named_params on internal __call). (ilutov) + . Fixed bug GH-12826 (Weird pointers issue in nested loops). (nielsdos) - FPM: . Fixed bug GH-12705 (Segmentation fault in fpm_status_export_to_zval). diff --git a/Zend/tests/gh12826.phpt b/Zend/tests/gh12826.phpt new file mode 100644 index 0000000000000..adfc3a747c35f --- /dev/null +++ b/Zend/tests/gh12826.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-12826 (Weird pointers issue in nested loops) +--FILE-- + 1, + 'b' => 2, + 'c' => 3, + 'd' => 4, +); + +unset($test['a']); +unset($test['b']); + +foreach($test as $k => &$v) { // Mind the reference! + echo "Pass $k : "; + + foreach($test as $kk => $vv) { + echo $test[$kk]; + if ($kk == $k) $test[$kk] = 0; + } + + echo "\n"; +} + +unset($v); +?> +--EXPECT-- +Pass c : 34 +Pass d : 04 diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 03683e3a1ea4e..6668c4c17c669 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -2407,7 +2407,7 @@ static zend_always_inline uint32_t zend_array_dup_elements(HashTable *source, Ha idx++; p++; } } else { - target->nNumUsed = source->nNumOfElements; + target->nNumUsed = source->nNumUsed; uint32_t iter_pos = zend_hash_iterators_lower_pos(target, idx); while (p != end) {