Commit 2a2e0e81285 for php.net
commit 2a2e0e81285e46dcbd9e08699553fe49a409d16d
Author: Niels Dossche <7771979+ndossche@users.noreply.github.com>
Date: Fri Jan 9 17:14:11 2026 +0100
Fix GH-20856: heap-use-after-free in SplDoublyLinkedList iterator when modifying during iteration
The element may be still in use in other places, so the linking pointers
should be kept consistent. If not consistent, the "move forward" code in
the sample test will read a stale, dangling pointer.
Closes GH-20885.
diff --git a/NEWS b/NEWS
index ab7bea69fb6..21f69b8895c 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,10 @@ PHP NEWS
. Fixed bug GH-18139 (Memory leak when overriding some settings
via readline_info()). (ndossche)
+- SPL:
+ . Fixed bug GH-20856 (heap-use-after-free in SplDoublyLinkedList iterator
+ when modifying during iteration). (ndossche)
+
- Standard:
. Fixed bug #74357 (lchown fails to change ownership of symlink with ZTS)
(Jakub Zelenka)
diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c
index 5a78db2921a..2ac7980a86c 100644
--- a/ext/spl/spl_dllist.c
+++ b/ext/spl/spl_dllist.c
@@ -764,11 +764,10 @@ PHP_METHOD(SplDoublyLinkedList, offsetUnset)
element = spl_ptr_llist_offset(intern->llist, index, intern->flags & SPL_DLLIST_IT_LIFO);
if (element != NULL) {
- /* connect the neightbors */
+ /* disconnect the neighbours */
if (element->prev) {
element->prev->next = element->next;
}
-
if (element->next) {
element->next->prev = element->prev;
}
@@ -782,6 +781,10 @@ PHP_METHOD(SplDoublyLinkedList, offsetUnset)
llist->tail = element->prev;
}
+ /* Keep consistency if element is kept alive. */
+ element->prev = NULL;
+ element->next = NULL;
+
/* finally, delete the element */
llist->count--;
diff --git a/ext/spl/tests/gh20856.phpt b/ext/spl/tests/gh20856.phpt
new file mode 100644
index 00000000000..8bc1b3c9582
--- /dev/null
+++ b/ext/spl/tests/gh20856.phpt
@@ -0,0 +1,26 @@
+--TEST--
+GH-20856 (heap-use-after-free in SplDoublyLinkedList iterator when modifying during iteration)
+--CREDITS--
+vi3tL0u1s
+iluuu1994
+--FILE--
+<?php
+$m = new SplStack;
+$m[] = new stdClass;
+$m[] = new stdClass;
+
+foreach ($m as $l) {
+ unset($m[0]);
+ unset($m[0]);
+}
+
+var_dump($m);
+?>
+--EXPECTF--
+object(SplStack)#%d (%d) {
+ ["flags":"SplDoublyLinkedList":private]=>
+ int(6)
+ ["dllist":"SplDoublyLinkedList":private]=>
+ array(0) {
+ }
+}