Skip to content

Commit d6b7bd0

Browse files
committed
Fix phpGH-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 phpGH-22066
1 parent 696ea50 commit d6b7bd0

3 files changed

Lines changed: 33 additions & 1 deletion

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ PHP NEWS
181181
with re-entrant getHash()). (Pratik Bhujel)
182182
. Fix bugs GH-8561, GH-8562, GH-8563, and GH-8564 (Fixing various
183183
SplFileObject iterator desync bugs). (iliaal)
184+
. Fix bug GH-22062 (SplDoublyLinkedList iterator UAF
185+
via destructor releasing next node). (David Carlier)
184186

185187
- Sqlite3:
186188
. Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche)

ext/spl/spl_dllist.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p
798798

799799
if (flags & SPL_DLLIST_IT_LIFO) {
800800
*traverse_pointer_ptr = old->prev;
801+
SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr);
801802
(*traverse_position_ptr)--;
802803

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

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

822824
SPL_LLIST_DELREF(old);
823-
SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr);
824825
}
825826
}
826827
/* }}} */

ext/spl/tests/gh22062.phpt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
GH-22062 (SplDoublyLinkedList iterator UAF via destructor releasing next node)
3+
--FILE--
4+
<?php
5+
$list = new SplDoublyLinkedList();
6+
$list->setIteratorMode(
7+
SplDoublyLinkedList::IT_MODE_FIFO |
8+
SplDoublyLinkedList::IT_MODE_DELETE
9+
);
10+
11+
$list->push(new class($list) {
12+
public function __construct(private SplDoublyLinkedList $list) {}
13+
public function __destruct() {
14+
if ($this->list->count() > 0) {
15+
$this->list->offsetUnset(0);
16+
}
17+
}
18+
});
19+
20+
$list->push(new stdClass());
21+
22+
foreach ($list as $item) {
23+
unset($item);
24+
}
25+
26+
var_dump($list->count());
27+
?>
28+
--EXPECT--
29+
int(0)

0 commit comments

Comments
 (0)