Closed chibinz closed 27 minutes ago
Thanks for the report! Reproduces for me. Likely this can be fixed in a similar way as https://github.com/php/php-src/pull/16346
I think we can just delay the destructor call.
diff --git a/Zend/tests/gh16464.phpt b/Zend/tests/gh16464.phpt
new file mode 100644
index 0000000000..75f34fc250
--- /dev/null
+++ b/Zend/tests/gh16464.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Use-after-free in SplDoublyLinkedList::offsetSet() when modifying list in destructor of overwritten object
+--FILE--
+<?php
+
+class C {
+ public $a;
+
+ function __destruct() {
+ global $list;
+ var_dump($list->pop());
+ }
+}
+
+$list = new SplDoublyLinkedList;
+$list->add(0, new C);
+$list[0] = 42;
+var_dump($list);
+
+?>
+--EXPECTF--
+int(42)
+object(SplDoublyLinkedList)#%d (2) {
+ ["flags":"SplDoublyLinkedList":private]=>
+ int(0)
+ ["dllist":"SplDoublyLinkedList":private]=>
+ array(0) {
+ }
+}
diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c
index 186b9a34c7..6592efc4e5 100644
--- a/ext/spl/spl_dllist.c
+++ b/ext/spl/spl_dllist.c
@@ -737,8 +737,10 @@ PHP_METHOD(SplDoublyLinkedList, offsetSet)
if (element != NULL) {
/* the element is replaced, delref the old one as in
* SplDoublyLinkedList::pop() */
- zval_ptr_dtor(&element->data);
+ zval garbage;
+ ZVAL_COPY_VALUE(&garbage, &element->data);
ZVAL_COPY(&element->data, value);
+ zval_ptr_dtor(&garbage);
} else {
zval_ptr_dtor(value);
zend_argument_error(spl_ce_OutOfRangeException, 1, "is an invalid offset");
I hadn't yet looked at the implementation. Indeed that's easier and looks right. The fix you propose can't apply to the min/maxheap though because there we can modify during element comparison, but that's not the case here.
Indeed, this only works here because we may fully delay the destructor, and there are no other possible side-effects. I'll open a PR.
Description
The following code:
Resulted in this output:
But I expected this output instead:
PHP Version
PHP 8.5.0-dev
Operating System
No response