Fix broken cache invalidation with deallocated and reallocated document node

The original caching implementation had an oversight in combination with
the new lifetime management in DOM for 8.3.
The modification counter is stored on the document object itself, but as
that can get deallocated when all references disappear, stale cache data
can be used. Normally this isn't a problem, unless getElementsByTagName is
called not on the document but on a child node. Fix it by moving caching
data into the ref object, which will outlive all nodes from a document
even if the document object disappears.

Closes GH-12338.
This commit is contained in:
Niels Dossche 2023-10-01 14:26:24 +02:00
parent 31a44c8ca7
commit eebc528cbf
8 changed files with 86 additions and 46 deletions

View file

@ -1293,13 +1293,7 @@ PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object,
object->node->_private = private_data;
}
} else {
if (UNEXPECTED(node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE)) {
php_libxml_doc_ptr *doc_ptr = emalloc(sizeof(php_libxml_doc_ptr));
doc_ptr->cache_tag.modification_nr = 1; /* iterators start at 0, such that they will start in an uninitialised state */
object->node = (php_libxml_node_ptr *) doc_ptr; /* downcast */
} else {
object->node = emalloc(sizeof(php_libxml_node_ptr));
}
object->node = emalloc(sizeof(php_libxml_node_ptr));
ret_refcount = 1;
object->node->node = node;
object->node->refcount = 1;
@ -1344,6 +1338,7 @@ PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object,
object->document->ptr = docp;
object->document->refcount = ret_refcount;
object->document->doc_props = NULL;
object->document->cache_tag.modification_nr = 1; /* iterators start at 0, such that they will start in an uninitialised state */
}
return ret_refcount;