dom: clear the xpath callback registrations before freeing them - #23621
dom: clear the xpath callback registrations before freeing them#23621iliaal wants to merge 1 commit into
Conversation
php_dom_xpath_callbacks_dtor() freed php_ns and each namespaces entry while leaving registry->php_ns and registry->namespaces pointing at them, and it then destroyed node_list, which runs node destructors. A destructor calling gc_collect_cycles() therefore reached php_dom_xpath_callback_ns_get_gc() through the still-set fields and iterated freed memory. Reachable from userland by calling DOMXPath::__construct() a second time on an object that has php:function registrations and a populated node list. Closes phpGH-23621
2518794 to
354146d
Compare
|
fix looks fine but is the test succeeding even with USE_ZEND_ALLOC=0 for you ? |
|
Yes on the patched build: 10 out of 10 through run-tests and 20 out of 20 running the reproducer directly, both with Unpatched it is flaky rather than clean. The reproducer aborts about half the time, 11 of 20, on the debug assertion at |
|
would like this one too, it should crash w/o your fix ...
class GcElement extends DOMElement {
public function __destruct() { gc_collect_cycles(); }
}
class Holder { public $self; public function cb($n) { return true; } }
$doc = new DOMDocument();
$doc->loadXML('<r><a/><b/><c/></r>');
$doc->registerNodeClass(DOMElement::class, GcElement::class);
$xp = new DOMXPath($doc);
$xp->registerNamespace('php', 'http://php.net/xpath');
$h = new Holder(); $h->self = $h;
$xp->registerPhpFunctions(['cb' => [$h, 'cb']]);
$xp->query('/r/*[php:function("cb", .)]');
unset($h);
$tmp = $xp; unset($tmp);
$xp->__construct($doc);
var_dump($xp->query('/r/a')->length);
... |
php_dom_xpath_callbacks_dtor() freed php_ns and each namespaces entry while leaving registry->php_ns and registry->namespaces pointing at them, and it then destroyed node_list, which runs node destructors. A destructor calling gc_collect_cycles() therefore reached php_dom_xpath_callback_ns_get_gc() through the still-set fields and iterated freed memory. Reachable from userland by calling DOMXPath::__construct() a second time on an object that has php:function registrations and a populated node list. Closes phpGH-23621
354146d to
75e510d
Compare
|
Added, and it is a much better pin than mine was. With Two changes to it. I put |
php_dom_xpath_callbacks_dtor()freedphp_nsand eachnamespacesentry but leftregistry->php_nsandregistry->namespacespointing at the freed memory, and it then destroyednode_list, which runs node destructors. A destructor that callsgc_collect_cycles()reachesphp_dom_xpath_callback_ns_get_gc()through those still-set fields and iterates freed memory.Reachable from userland by calling
DOMXPath::__construct()a second time on an object that already hasphp:functionregistrations and a populated node list, which is the path atext/dom/xpath.cthat tears the registry down on a live object.