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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user