1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Merge branch 'PHP-8.3'

* PHP-8.3:
  Fix broken cache invalidation with deallocated and reallocated document node
This commit is contained in:
Niels Dossche
2023-10-01 17:07:11 +02:00
7 changed files with 84 additions and 46 deletions

View File

@@ -818,7 +818,7 @@ PHP_METHOD(DOMDocument, importNode)
}
}
php_libxml_invalidate_node_list_cache_from_doc(docp);
php_libxml_invalidate_node_list_cache(intern->document);
DOM_RET_OBJ((xmlNodePtr) retnodep, &ret, intern);
}
@@ -1031,7 +1031,7 @@ bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, x
{
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
if (nodep->doc != new_document) {
php_libxml_invalidate_node_list_cache_from_doc(new_document);
php_libxml_invalidate_node_list_cache(dom_object_new_document->document);
/* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
* and since this unlink it; the owner element will be unset (i.e. parentNode). */
@@ -1101,7 +1101,7 @@ PHP_METHOD(DOMDocument, normalizeDocument)
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
php_libxml_invalidate_node_list_cache_from_doc(docp);
php_libxml_invalidate_node_list_cache(intern->document);
dom_normalize((xmlNodePtr) docp);
}
@@ -1327,7 +1327,7 @@ static void dom_finish_loading_document(zval *this, zval *return_value, xmlDocPt
xmlDocPtr docp = (xmlDocPtr) dom_object_get_node(intern);
dom_doc_propsptr doc_prop = NULL;
if (docp != NULL) {
const php_libxml_doc_ptr *doc_ptr = docp->_private;
const php_libxml_ref_obj *doc_ptr = intern->document;
ZEND_ASSERT(doc_ptr != NULL); /* Must exist, we have a document */
old_modification_nr = doc_ptr->cache_tag.modification_nr;
php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
@@ -1348,9 +1348,8 @@ static void dom_finish_loading_document(zval *this, zval *return_value, xmlDocPt
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
/* Since iterators should invalidate, we need to start the modification number from the old counter */
if (old_modification_nr != 0) {
php_libxml_doc_ptr* doc_ptr = (php_libxml_doc_ptr*) ((php_libxml_node_object*) intern)->node; /* downcast */
doc_ptr->cache_tag.modification_nr = old_modification_nr;
php_libxml_invalidate_node_list_cache(doc_ptr);
intern->document->cache_tag.modification_nr = old_modification_nr;
php_libxml_invalidate_node_list_cache(intern->document);
}
RETURN_TRUE;
@@ -1611,7 +1610,7 @@ PHP_METHOD(DOMDocument, xinclude)
php_dom_remove_xinclude_nodes(root);
}
php_libxml_invalidate_node_list_cache_from_doc(docp);
php_libxml_invalidate_node_list_cache(intern->document);
if (err) {
RETVAL_LONG(err);

View File

@@ -201,7 +201,7 @@ zend_result dom_node_node_value_write(dom_object *obj, zval *newval)
break;
}
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
php_libxml_invalidate_node_list_cache(obj->document);
zend_string_release_ex(str, 0);
return SUCCESS;
@@ -794,7 +794,7 @@ zend_result dom_node_text_content_write(dom_object *obj, zval *newval)
return FAILURE;
}
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
php_libxml_invalidate_node_list_cache(obj->document);
/* Typed property, this is already a string */
ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
@@ -919,7 +919,7 @@ PHP_METHOD(DOMNode, insertBefore)
php_libxml_increment_doc_ref((php_libxml_node_object *)childobj, NULL);
}
php_libxml_invalidate_node_list_cache_from_doc(parentp->doc);
php_libxml_invalidate_node_list_cache(intern->document);
if (ref != NULL) {
DOM_GET_OBJ(refp, ref, xmlNodePtr, refpobj);
@@ -1124,7 +1124,7 @@ PHP_METHOD(DOMNode, replaceChild)
nodep->doc->intSubset = (xmlDtd *) newchild;
}
}
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
php_libxml_invalidate_node_list_cache(intern->document);
DOM_RET_OBJ(oldchild, &ret, intern);
}
/* }}} end dom_node_replace_child */
@@ -1166,7 +1166,7 @@ PHP_METHOD(DOMNode, removeChild)
}
xmlUnlinkNode(child);
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
php_libxml_invalidate_node_list_cache(intern->document);
DOM_RET_OBJ(child, &ret, intern);
}
/* }}} end dom_node_remove_child */
@@ -1271,7 +1271,7 @@ PHP_METHOD(DOMNode, appendChild)
dom_reconcile_ns(nodep->doc, new_child);
}
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
php_libxml_invalidate_node_list_cache(intern->document);
DOM_RET_OBJ(new_child, &ret, intern);
return;
@@ -1387,7 +1387,7 @@ PHP_METHOD(DOMNode, normalize)
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
php_libxml_invalidate_node_list_cache(intern->document);
dom_normalize(nodep);

View File

@@ -309,7 +309,7 @@ void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc)
return;
}
php_libxml_invalidate_node_list_cache_from_doc(parentNode->doc);
php_libxml_invalidate_node_list_cache(context->document);
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
@@ -353,7 +353,7 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc)
return;
}
php_libxml_invalidate_node_list_cache_from_doc(parentNode->doc);
php_libxml_invalidate_node_list_cache(context->document);
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
@@ -404,7 +404,7 @@ void dom_parent_node_after(dom_object *context, zval *nodes, uint32_t nodesc)
doc = prevsib->doc;
php_libxml_invalidate_node_list_cache_from_doc(doc);
php_libxml_invalidate_node_list_cache(context->document);
/* Spec step 4: convert nodes into fragment */
fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
@@ -456,7 +456,7 @@ void dom_parent_node_before(dom_object *context, zval *nodes, uint32_t nodesc)
doc = nextsib->doc;
php_libxml_invalidate_node_list_cache_from_doc(doc);
php_libxml_invalidate_node_list_cache(context->document);
/* Spec step 4: convert nodes into fragment */
fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
@@ -523,7 +523,7 @@ void dom_child_node_remove(dom_object *context)
return;
}
php_libxml_invalidate_node_list_cache_from_doc(child->doc);
php_libxml_invalidate_node_list_cache(context->document);
xmlUnlinkNode(child);
}
@@ -557,7 +557,7 @@ void dom_child_replace_with(dom_object *context, zval *nodes, uint32_t nodesc)
}
xmlDocPtr doc = parentNode->doc;
php_libxml_invalidate_node_list_cache_from_doc(doc);
php_libxml_invalidate_node_list_cache(context->document);
/* Spec step 4: convert nodes into fragment */
xmlNodePtr fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
@@ -602,7 +602,7 @@ void dom_parent_node_replace_children(dom_object *context, zval *nodes, uint32_t
return;
}
php_libxml_invalidate_node_list_cache_from_doc(thisp->doc);
php_libxml_invalidate_node_list_cache(context->document);
dom_remove_all_children(thisp);

View File

@@ -191,7 +191,7 @@ int php_dom_get_nodelist_length(dom_object *obj);
#define DOM_NODELIST 0
#define DOM_NAMEDNODEMAP 1
static zend_always_inline bool php_dom_is_cache_tag_stale_from_doc_ptr(const php_libxml_cache_tag *cache_tag, const php_libxml_doc_ptr *doc_ptr)
static zend_always_inline bool php_dom_is_cache_tag_stale_from_doc_ptr(const php_libxml_cache_tag *cache_tag, const php_libxml_ref_obj *doc_ptr)
{
ZEND_ASSERT(cache_tag != NULL);
ZEND_ASSERT(doc_ptr != NULL);
@@ -206,15 +206,26 @@ static zend_always_inline bool php_dom_is_cache_tag_stale_from_doc_ptr(const php
static zend_always_inline bool php_dom_is_cache_tag_stale_from_node(const php_libxml_cache_tag *cache_tag, const xmlNodePtr node)
{
ZEND_ASSERT(node != NULL);
return !node->doc || !node->doc->_private || php_dom_is_cache_tag_stale_from_doc_ptr(cache_tag, node->doc->_private);
php_libxml_node_ptr *private = node->_private;
if (!private) {
return true;
}
php_libxml_node_object *object_private = private->_private;
if (!object_private || !object_private->document) {
return true;
}
return php_dom_is_cache_tag_stale_from_doc_ptr(cache_tag, object_private->document);
}
static zend_always_inline void php_dom_mark_cache_tag_up_to_date_from_node(php_libxml_cache_tag *cache_tag, const xmlNodePtr node)
{
ZEND_ASSERT(cache_tag != NULL);
if (node->doc && node->doc->_private) {
const php_libxml_doc_ptr* doc_ptr = node->doc->_private;
cache_tag->modification_nr = doc_ptr->cache_tag.modification_nr;
php_libxml_node_ptr *private = node->_private;
if (private) {
php_libxml_node_object *object_private = private->_private;
if (object_private->document) {
cache_tag->modification_nr = object_private->document->cache_tag.modification_nr;
}
}
}

View File

@@ -0,0 +1,31 @@
--TEST--
getElementsByTagName() liveness with deallocated document
--EXTENSIONS--
dom
--FILE--
<?php
$dom = new DOMDocument;
$dom->loadXML(<<<XML
<?xml version="1.0"?>
<container>
<p>1</p><p>2</p><p>3</p>
</container>
XML);
$ps = $dom->documentElement->getElementsByTagName('p');
$second = $ps->item(1);
var_dump($second->textContent);
var_dump($ps->length);
unset($dom);
$dom = $second->ownerDocument;
$second->parentNode->appendChild($dom->createElement('p', '4'));
var_dump($ps->length);
?>
--EXPECT--
string(1) "2"
int(3)
int(4)

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;

View File

@@ -57,10 +57,15 @@ typedef struct _libxml_doc_props {
bool recover;
} libxml_doc_props;
typedef struct {
size_t modification_nr;
} php_libxml_cache_tag;
typedef struct _php_libxml_ref_obj {
void *ptr;
int refcount;
libxml_doc_props *doc_props;
php_libxml_cache_tag cache_tag;
} php_libxml_ref_obj;
typedef struct _php_libxml_node_ptr {
@@ -69,16 +74,6 @@ typedef struct _php_libxml_node_ptr {
void *_private;
} php_libxml_node_ptr;
typedef struct {
size_t modification_nr;
} php_libxml_cache_tag;
/* extends php_libxml_node_ptr */
typedef struct {
php_libxml_node_ptr node_ptr;
php_libxml_cache_tag cache_tag;
} php_libxml_doc_ptr;
typedef struct _php_libxml_node_object {
php_libxml_node_ptr *node;
php_libxml_ref_obj *document;
@@ -91,8 +86,11 @@ static inline php_libxml_node_object *php_libxml_node_fetch_object(zend_object *
return (php_libxml_node_object *)((char*)(obj) - obj->handlers->offset);
}
static zend_always_inline void php_libxml_invalidate_node_list_cache(php_libxml_doc_ptr *doc_ptr)
static zend_always_inline void php_libxml_invalidate_node_list_cache(php_libxml_ref_obj *doc_ptr)
{
if (!doc_ptr) {
return;
}
#if SIZEOF_SIZE_T == 8
/* If one operation happens every nanosecond, then it would still require 584 years to overflow
* the counter. So we'll just assume this never happens. */
@@ -108,7 +106,11 @@ static zend_always_inline void php_libxml_invalidate_node_list_cache(php_libxml_
static zend_always_inline void php_libxml_invalidate_node_list_cache_from_doc(xmlDocPtr docp)
{
if (docp && docp->_private) { /* docp is NULL for detached nodes */
php_libxml_invalidate_node_list_cache((php_libxml_doc_ptr *)docp->_private);
php_libxml_node_ptr *private = docp->_private;
php_libxml_node_object *object_private = private->_private;
if (object_private) {
php_libxml_invalidate_node_list_cache(object_private->document);
}
}
}