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 crash when calling childNodes next() when iterator is exhausted
  Fix references not handled correctly in C14N
  Fix crashes when entity declaration is removed while still having entity references
This commit is contained in:
Niels Dossche
2024-04-30 22:53:48 +02:00
11 changed files with 244 additions and 8 deletions

View File

@@ -114,8 +114,7 @@ static zend_result php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */
zval *php_dom_iterator_current_data(zend_object_iterator *iter) /* {{{ */
{
php_dom_iterator *iterator = (php_dom_iterator *)iter;
return &iterator->curobj;
return Z_ISUNDEF(iterator->curobj) ? NULL : &iterator->curobj;
}
/* }}} */

View File

@@ -89,6 +89,11 @@ zend_result dom_entity_actual_encoding_read(dom_object *obj, zval *retval);
zend_result dom_entity_encoding_read(dom_object *obj, zval *retval);
zend_result dom_entity_version_read(dom_object *obj, zval *retval);
/* entity reference properties */
int dom_entity_reference_child_read(dom_object *obj, zval *retval);
int dom_entity_reference_text_content_read(dom_object *obj, zval *retval);
int dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval);
/* namednodemap properties */
zend_result dom_namednodemap_length_read(dom_object *obj, zval *retval);

View File

@@ -22,6 +22,7 @@
#include "php.h"
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include "dom_properties.h"
/*
* class DOMEntityReference extends DOMNode
@@ -65,4 +66,63 @@ PHP_METHOD(DOMEntityReference, __construct)
}
/* }}} end DOMEntityReference::__construct */
/* The following property handlers are necessary because of special lifetime management with entities and entity
* references. The issue is that entity references hold a reference to an entity declaration, but don't
* register that reference anywhere. When the entity declaration disappears we have no way of notifying the
* entity references. Override the property handlers for the declaration-accessing properties to fix this problem. */
xmlEntityPtr dom_entity_reference_fetch_and_sync_declaration(xmlNodePtr reference)
{
xmlEntityPtr entity = xmlGetDocEntity(reference->doc, reference->name);
reference->children = (xmlNodePtr) entity;
reference->last = (xmlNodePtr) entity;
reference->content = entity ? entity->content : NULL;
return entity;
}
int dom_entity_reference_child_read(dom_object *obj, zval *retval)
{
xmlNodePtr nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, true);
return FAILURE;
}
xmlEntityPtr entity = dom_entity_reference_fetch_and_sync_declaration(nodep);
if (entity == NULL) {
ZVAL_NULL(retval);
return SUCCESS;
}
php_dom_create_object((xmlNodePtr) entity, retval, obj);
return SUCCESS;
}
int dom_entity_reference_text_content_read(dom_object *obj, zval *retval)
{
xmlNodePtr nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, true);
return FAILURE;
}
dom_entity_reference_fetch_and_sync_declaration(nodep);
return dom_node_text_content_read(obj, retval);
}
int dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval)
{
xmlNodePtr nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, true);
return FAILURE;
}
dom_entity_reference_fetch_and_sync_declaration(nodep);
return dom_node_child_nodes_read(obj, retval);
}
#endif

View File

@@ -2131,7 +2131,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
char *xquery;
/* Find "query" key */
tmp = zend_hash_find(ht, ZSTR_KNOWN(ZEND_STR_QUERY));
tmp = zend_hash_find_deref(ht, ZSTR_KNOWN(ZEND_STR_QUERY));
if (!tmp) {
/* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */
zend_argument_value_error(3 + mode, "must have a \"query\" key");
@@ -2147,12 +2147,13 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
ctxp = xmlXPathNewContext(docp);
ctxp->node = nodep;
tmp = zend_hash_str_find(ht, "namespaces", sizeof("namespaces")-1);
tmp = zend_hash_str_find_deref(ht, "namespaces", sizeof("namespaces")-1);
if (tmp && Z_TYPE_P(tmp) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(tmp))) {
zval *tmpns;
zend_string *prefix;
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), prefix, tmpns) {
ZVAL_DEREF(tmpns);
if (Z_TYPE_P(tmpns) == IS_STRING) {
if (prefix) {
xmlXPathRegisterNs(ctxp, BAD_CAST ZSTR_VAL(prefix), BAD_CAST Z_STRVAL_P(tmpns));

View File

@@ -50,6 +50,16 @@ static zend_always_inline void reset_objmap_cache(dom_nnodemap_object *objmap)
objmap->cached_length = -1;
}
static xmlNodePtr dom_nodelist_iter_start_first_child(xmlNodePtr nodep)
{
if (nodep->type == XML_ENTITY_REF_NODE) {
/* See entityreference.c */
dom_entity_reference_fetch_and_sync_declaration(nodep);
}
return nodep->children;
}
zend_long php_dom_get_nodelist_length(dom_object *obj)
{
dom_nnodemap_object *objmap = (dom_nnodemap_object *) obj->ptr;
@@ -84,7 +94,7 @@ zend_long php_dom_get_nodelist_length(dom_object *obj)
int count = 0;
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
xmlNodePtr curnode = nodep->children;
xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep);
if (curnode) {
count++;
while (curnode->next != NULL) {
@@ -170,7 +180,7 @@ void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long
int count = 0;
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
if (restart) {
nodep = nodep->children;
nodep = dom_nodelist_iter_start_first_child(nodep);
}
while (count < relative_index && nodep != NULL) {
count++;

View File

@@ -107,6 +107,8 @@ static HashTable dom_documentfragment_prop_handlers;
static HashTable dom_modern_documentfragment_prop_handlers;
static HashTable dom_node_prop_handlers;
static HashTable dom_modern_node_prop_handlers;
static HashTable dom_entity_reference_prop_handlers;
static HashTable dom_modern_entity_reference_prop_handlers;
static HashTable dom_nodelist_prop_handlers;
static HashTable dom_namednodemap_prop_handlers;
static HashTable dom_characterdata_prop_handlers;
@@ -1146,12 +1148,26 @@ PHP_MINIT_FUNCTION(dom)
dom_entityreference_class_entry = register_class_DOMEntityReference(dom_node_class_entry);
dom_entityreference_class_entry->create_object = dom_objects_new;
dom_entityreference_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_entityreference_class_entry->name, &dom_node_prop_handlers);
zend_hash_init(&dom_entity_reference_prop_handlers, 0, NULL, NULL, true);
zend_hash_merge(&dom_entity_reference_prop_handlers, &dom_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "firstChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "lastChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "textContent", dom_entity_reference_text_content_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "childNodes", dom_entity_reference_child_nodes_read, NULL);
zend_hash_add_new_ptr(&classes, dom_entityreference_class_entry->name, &dom_entity_reference_prop_handlers);
dom_modern_entityreference_class_entry = register_class_DOM_EntityReference(dom_modern_node_class_entry);
dom_modern_entityreference_class_entry->create_object = dom_objects_new;
dom_modern_entityreference_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_modern_entityreference_class_entry->name, &dom_modern_node_prop_handlers);
zend_hash_init(&dom_modern_entity_reference_prop_handlers, 0, NULL, NULL, true);
zend_hash_merge(&dom_modern_entity_reference_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "firstChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "lastChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "textContent", dom_entity_reference_text_content_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "childNodes", dom_entity_reference_child_nodes_read, NULL);
zend_hash_add_new_ptr(&classes, dom_modern_entityreference_class_entry->name, &dom_modern_entity_reference_prop_handlers);
dom_processinginstruction_class_entry = register_class_DOMProcessingInstruction(dom_node_class_entry);
dom_processinginstruction_class_entry->create_object = dom_objects_new;
@@ -1240,6 +1256,8 @@ PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */
zend_hash_destroy(&dom_modern_documentfragment_prop_handlers);
zend_hash_destroy(&dom_node_prop_handlers);
zend_hash_destroy(&dom_modern_node_prop_handlers);
zend_hash_destroy(&dom_entity_reference_prop_handlers);
zend_hash_destroy(&dom_modern_entity_reference_prop_handlers);
zend_hash_destroy(&dom_namespace_node_prop_handlers);
zend_hash_destroy(&dom_nodelist_prop_handlers);
zend_hash_destroy(&dom_namednodemap_prop_handlers);

View File

@@ -173,6 +173,7 @@ xmlChar *php_dom_libxml_fix_file_path(xmlChar *path);
void dom_document_convert_to_modern(php_libxml_ref_obj *document, xmlDocPtr lxml_doc);
dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent);
xmlDocPtr php_dom_create_html_doc(void);
xmlEntityPtr dom_entity_reference_fetch_and_sync_declaration(xmlNodePtr reference);
xmlChar *dom_attr_value(const xmlAttr *attr, bool *free);
bool dom_compare_value(const xmlAttr *attr, const xmlChar *value);

View File

@@ -0,0 +1,41 @@
--TEST--
Test: Canonicalization - C14N() with references
--EXTENSIONS--
dom
--FILE--
<?php
// Adapted from canonicalization.phpt
$xml = <<<EOXML
<?xml version="1.0" encoding="ISO-8859-1" ?>
<foo xmlns="http://www.example.com/ns/foo"
xmlns:fubar="http://www.example.com/ns/fubar" xmlns:test="urn::test"><contain>
<bar><test1 /></bar>
<bar><test2 /></bar>
<fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test3 /></fubar:bar>
<fubar:bar><test4 /></fubar:bar>
</contain>
</foo>
EOXML;
$dom = new DOMDocument();
$dom->loadXML($xml);
$doc = $dom->documentElement->firstChild;
$xpath = [
'query' => '(//a:contain | //a:bar | .//namespace::*)',
'namespaces' => ['a' => 'http://www.example.com/ns/foo'],
];
$prefixes = ['test'];
foreach ($xpath['namespaces'] as $k => &$v);
unset($v);
foreach ($xpath as $k => &$v);
unset($v);
foreach ($prefixes as $k => &$v);
unset($v);
echo $doc->C14N(true, false, $xpath, $prefixes);
?>
--EXPECT--
<contain xmlns="http://www.example.com/ns/foo"><bar></bar><bar></bar></contain>

View File

@@ -0,0 +1,25 @@
--TEST--
Crash in childNodes iterator current()
--EXTENSIONS--
dom
--FILE--
<?php
$dom = new DOMDocument;
$dom->loadXML('<foo>foo1</foo>');
$nodes = $dom->documentElement->childNodes;
$iter = $nodes->getIterator();
var_dump($iter->valid());
var_dump($iter->current()?->wholeText);
$iter->next();
var_dump($iter->valid());
var_dump($iter->current()?->wholeText);
?>
--EXPECT--
bool(true)
string(4) "foo1"
bool(false)
NULL

View File

@@ -0,0 +1,41 @@
--TEST--
Entity references with stale entity declaration 01
--EXTENSIONS--
dom
--FILE--
<?php
$dom = new DOMDocument;
$dom->loadXML(<<<XML
<!DOCTYPE foo [
<!ENTITY foo "bar">
]>
<foo>&foo;</foo>
XML);
$ref = $dom->documentElement->firstChild;
$decl = $ref->firstChild;
$nodes = $ref->childNodes;
$dom->removeChild($dom->doctype);
unset($decl);
var_dump($nodes);
var_dump($ref->firstChild);
var_dump($ref->lastChild);
var_dump($ref->textContent);
var_dump($ref->childNodes);
?>
--EXPECT--
object(DOMNodeList)#4 (1) {
["length"]=>
int(0)
}
NULL
NULL
string(0) ""
object(DOMNodeList)#2 (1) {
["length"]=>
int(0)
}

View File

@@ -0,0 +1,35 @@
--TEST--
Entity references with stale entity declaration 02
--EXTENSIONS--
dom
--FILE--
<?php
$dom = new DOMDocument;
$dom->loadXML(<<<XML
<!DOCTYPE foo [
<!ENTITY foo1 "bar1">
<!ENTITY foo2 "bar2">
<!ENTITY foo3 "bar3">
]>
<foo>&foo1;</foo>
XML);
$ref = $dom->documentElement->firstChild;
$decl = $ref->firstChild;
$nodes = $ref->childNodes;
$iter = $nodes->getIterator();
$iter->next();
$dom->removeChild($dom->doctype);
unset($decl);
try {
var_dump($iter->current()->publicId);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
NULL