[php-src] master: Merge branch 'PHP-8.4' into PHP-8.5

Ilia Alshanetsky <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-06-27T07:47:47-04:00

Commit: https://github.com/php/php-src/commit/ec5a4f4912a6ab759676bfb119d3a3de6c3ab248
Raw diff: https://github.com/php/php-src/commit/ec5a4f4912a6ab759676bfb119d3a3de6c3ab248.diff

Merge branch 'PHP-8.4' into PHP-8.5

* PHP-8.4:
  Fix use-after-free in RecursiveIteratorIterator on reentry

Changed paths:
  A  ext/spl/tests/recursiveiteratoriterator_rewind_during_next.phpt
  M  ext/spl/spl_iterators.c


Diff:

diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c
index 1ae4dae1eaa3..0c328048f605 100644
--- a/ext/spl/spl_iterators.c
+++ b/ext/spl/spl_iterators.c
@@ -273,6 +273,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv
 						zend_clear_exception();
 					}
 				}
+				iterator = object->iterators[object->level].iterator;
 				ZEND_FALLTHROUGH;
 			case RS_START:
 				if (iterator->funcs->valid(iterator) == FAILURE) {
diff --git a/ext/spl/tests/recursiveiteratoriterator_rewind_during_next.phpt b/ext/spl/tests/recursiveiteratoriterator_rewind_during_next.phpt
new file mode 100644
index 000000000000..3c9863e81a30
--- /dev/null
+++ b/ext/spl/tests/recursiveiteratoriterator_rewind_during_next.phpt
@@ -0,0 +1,41 @@
+--TEST--
+RecursiveIteratorIterator: rewind() re-entered from an inner next() must not use-after-free
+--FILE--
+<?php
+class Reenter implements RecursiveIterator {
+    public $data; public $pos = 0; public $rii; public $depth;
+    public static bool $fired = false;
+    function __construct(array $d, $depth = 0) { $this->data = $d; $this->depth = $depth; }
+    function current(): mixed { return $this->data[$this->pos] ?? null; }
+    function key(): mixed { return $this->pos; }
+    function next(): void {
+        $this->pos++;
+        if ($this->rii && $this->depth === 1 && $this->pos === 1 && !self::$fired) {
+            self::$fired = true;
+            $this->rii->rewind();
+        }
+    }
+    function rewind(): void { $this->pos = 0; }
+    function valid(): bool { return $this->pos < count($this->data); }
+    function hasChildren(): bool { return is_array($this->current()); }
+    function getChildren(): RecursiveIterator {
+        $c = new Reenter($this->current(), $this->depth + 1);
+        $c->rii = $this->rii;
+        return $c;
+    }
+}
+$root = new Reenter([[10, 11], [20, 21]]);
+$rii = new RecursiveIteratorIterator($root, RecursiveIteratorIterator::SELF_FIRST);
+$root->rii = $rii;
+$seen = [];
+foreach ($rii as $v) {
+    if (is_array($v)) { $v = '[' . implode(',', $v) . ']'; }
+    $seen[] = $v;
+    if (count($seen) > 20) { $seen[] = '...'; break; }
+}
+echo implode(' ', $seen), "\n";
+echo "done\n";
+?>
+--EXPECT--
+[10,11] 10 [10,11] 10 11 [20,21] 20 21
+done
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.