Commit d6b7bd0f778 for php.net

commit d6b7bd0f778cd45c0bae91d6e334fdf9f7049199
Author: David Carlier <devnexen@gmail.com>
Date:   Sat May 16 21:05:15 2026 +0100

    Fix GH-22062: SplDoublyLinkedList iterator UAF via destructor releasing next node.

    Pin the new traverse target via SPL_LLIST_CHECK_ADDREF before the
    shift/pop destructor runs. Otherwise a destructor that unlinks the
    next node (e.g. offsetUnset) frees it, leaving the iterator with a
    dangling pointer.

    close GH-22066

diff --git a/NEWS b/NEWS
index 239becd194f..1200d086067 100644
--- a/NEWS
+++ b/NEWS
@@ -181,6 +181,8 @@ PHP                                                                        NEWS
     with re-entrant getHash()). (Pratik Bhujel)
   . Fix bugs GH-8561, GH-8562, GH-8563, and GH-8564 (Fixing various
     SplFileObject iterator desync bugs). (iliaal)
+  . Fix bug GH-22062 (SplDoublyLinkedList iterator UAF
+    via destructor releasing next node). (David Carlier)

 - Sqlite3:
   . Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche)
diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c
index eaadec03cf8..be4f13cfcfc 100644
--- a/ext/spl/spl_dllist.c
+++ b/ext/spl/spl_dllist.c
@@ -798,6 +798,7 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p

 		if (flags & SPL_DLLIST_IT_LIFO) {
 			*traverse_pointer_ptr = old->prev;
+			SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr);
 			(*traverse_position_ptr)--;

 			if (flags & SPL_DLLIST_IT_DELETE) {
@@ -808,6 +809,7 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p
 			}
 		} else {
 			*traverse_pointer_ptr = old->next;
+			SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr);

 			if (flags & SPL_DLLIST_IT_DELETE) {
 				zval prev;
@@ -820,7 +822,6 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p
 		}

 		SPL_LLIST_DELREF(old);
-		SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr);
 	}
 }
 /* }}} */
diff --git a/ext/spl/tests/gh22062.phpt b/ext/spl/tests/gh22062.phpt
new file mode 100644
index 00000000000..ea67a9983a3
--- /dev/null
+++ b/ext/spl/tests/gh22062.phpt
@@ -0,0 +1,29 @@
+--TEST--
+GH-22062 (SplDoublyLinkedList iterator UAF via destructor releasing next node)
+--FILE--
+<?php
+$list = new SplDoublyLinkedList();
+$list->setIteratorMode(
+    SplDoublyLinkedList::IT_MODE_FIFO |
+    SplDoublyLinkedList::IT_MODE_DELETE
+);
+
+$list->push(new class($list) {
+    public function __construct(private SplDoublyLinkedList $list) {}
+    public function __destruct() {
+        if ($this->list->count() > 0) {
+            $this->list->offsetUnset(0);
+        }
+    }
+});
+
+$list->push(new stdClass());
+
+foreach ($list as $item) {
+    unset($item);
+}
+
+var_dump($list->count());
+?>
+--EXPECT--
+int(0)