Description
The following code:
<?php
$document = new DOMDocument();
$root = $document->appendChild($document->createElement('root'));
$text = $root->appendChild($document->createTextNode('self-insertion'));
// Use the same attached DOMText as both the child and insertion reference.
$root->insertBefore($text, $text);
// Reattach the corrupted node so document teardown traverses it.
$root->appendChild($text);
unset($text, $root, $document);
Produces a UAF on libxml2 2.9.14 and double free on libxml2. 2.9.13. insertBefore($n, $n) unlinks $n first, then builds the new position out of $n's parent and sibling fields that the unlink just cleared, so $n ends up outside the document and pointing at itself as its own next sibling.
Asan trace:
==70244==ERROR: AddressSanitizer: attempting double-free on 0x602000002bd0 in thread T0:
#0 0x000107055258 in free+0x7c (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x41258)
#1 0x000191105094 in xmlFreeNodeList+0x230 (libxml2.2.dylib:arm64e+0x11094)
#2 0x000191104a50 in xmlFreeDoc+0xac (libxml2.2.dylib:arm64e+0x10a50)
#3 0x000104b6da14 in php_libxml_decrement_doc_ref_directly libxml.c:1395
#4 0x000104b6db28 in php_libxml_decrement_doc_ref libxml.c:1415
#5 0x000104d5be0c in dom_objects_free_storage php_dom.c:1500
Note that the reproducer is kind of exotic with $root->insertBefore($text, $text);, and another reproducer is available, with less exotic code and its related Asan trace:
<?php
$d = new DOMDocument();
$d->loadXML('<root><p>solo</p></root>');
$p = $d->getElementsByTagName('p')->item(0);
$t = $p->firstChild;
$p->insertBefore($p->firstChild, $p->lastChild);
$p->appendChild($t);
unset($t, $p, $d);
The issue is that $p only has one child, and firstChild === lastChild here
==70241==ERROR: AddressSanitizer: attempting double-free on 0x602000002c10 in thread T0:
#0 0x000106995258 in free+0x7c (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x41258)
#1 0x000191105094 in xmlFreeNodeList+0x230 (libxml2.2.dylib:arm64e+0x11094)
#2 0x000191104a50 in xmlFreeDoc+0xac (libxml2.2.dylib:arm64e+0x10a50)
#3 0x0001043c5a14 in php_libxml_decrement_doc_ref_directly libxml.c:1395
#4 0x0001043c5b28 in php_libxml_decrement_doc_ref libxml.c:1415
#5 0x0001045b3e0c in dom_objects_free_storage php_dom.c:1500
#6 0x0001051da578 in zend_objects_store_del zend_objects_API.c:193
#7 0x0001050b1760 in ZEND_UNSET_CV_SPEC_CV_UNUSED_TAILCALL_HANDLER zend_vm_execute.h:102283
#8 0x000104e857c8 in execute_ex zend_vm_execute.h:110551
#9 0x000104e85f24 in zend_execute zend_vm_execute.h:115989
PHP Version
Operating System
No response
Description
The following code:
Produces a UAF on libxml2 2.9.14 and double free on libxml2. 2.9.13.
insertBefore($n, $n)unlinks$nfirst, then builds the new position out of$n's parent and sibling fields that the unlink just cleared, so$nends up outside the document and pointing at itself as its own next sibling.Asan trace:
Note that the reproducer is kind of exotic with
$root->insertBefore($text, $text);, and another reproducer is available, with less exotic code and its related Asan trace:The issue is that
$ponly has one child, andfirstChild === lastChildherePHP Version
Operating System
No response