Skip to content

Commit 8820a10

Browse files
committed
Fix uaf in SplDoublyLinkedList::offsetSet()
Write to the new offset before calling the destructor of the previous value. Fixes GH-16464 Closes GH-16466
1 parent 2bdf2f9 commit 8820a10

File tree

3 files changed

+34
-1
lines changed

3 files changed

+34
-1
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ PHP NEWS
6868

6969
- SPL:
7070
. Fixed bug GH-16337 (Use-after-free in SplHeap). (nielsdos)
71+
. Fixed bug GH-16464 (Use-after-free in SplDoublyLinkedList::offsetSet()).
72+
(ilutov)
7173

7274
- Standard:
7375
. Fixed bug GH-16293 (Failed assertion when throwing in assert() callback with

ext/spl/spl_dllist.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -737,8 +737,10 @@ PHP_METHOD(SplDoublyLinkedList, offsetSet)
737737
if (element != NULL) {
738738
/* the element is replaced, delref the old one as in
739739
* SplDoublyLinkedList::pop() */
740-
zval_ptr_dtor(&element->data);
740+
zval garbage;
741+
ZVAL_COPY_VALUE(&garbage, &element->data);
741742
ZVAL_COPY(&element->data, value);
743+
zval_ptr_dtor(&garbage);
742744
} else {
743745
zval_ptr_dtor(value);
744746
zend_argument_error(spl_ce_OutOfRangeException, 1, "is an invalid offset");

ext/spl/tests/gh16464.phpt

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
GH-16464: Use-after-free in SplDoublyLinkedList::offsetSet() when modifying list in destructor of overwritten object
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
public $a;
8+
9+
function __destruct() {
10+
global $list;
11+
var_dump($list->pop());
12+
}
13+
}
14+
15+
$list = new SplDoublyLinkedList;
16+
$list->add(0, new C);
17+
$list[0] = 42;
18+
var_dump($list);
19+
20+
?>
21+
--EXPECTF--
22+
int(42)
23+
object(SplDoublyLinkedList)#%d (2) {
24+
["flags":"SplDoublyLinkedList":private]=>
25+
int(0)
26+
["dllist":"SplDoublyLinkedList":private]=>
27+
array(0) {
28+
}
29+
}

0 commit comments

Comments
 (0)