mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Throw instead of silently failing when creating a too long text node
Lower branches suffer from this as well but we cannot change the behaviour there. We also add NULL checks to check for allocation failure. Closes GH-15014.
This commit is contained in:
2
NEWS
2
NEWS
@@ -11,6 +11,8 @@ PHP NEWS
|
||||
|
||||
- DOM:
|
||||
. Fix trampoline leak in xpath callables. (nielsdos)
|
||||
. Throw instead of silently failing when creating a too long text node in
|
||||
(DOM)ParentNode and (DOM)ChildNode. (nielsdos)
|
||||
|
||||
- OpenSSL:
|
||||
. Bumped minimum required OpenSSL version to 1.1.0. (cmb)
|
||||
|
||||
@@ -88,6 +88,11 @@ zend_result dom_parent_node_child_element_count(dom_object *obj, zval *retval)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static ZEND_COLD void dom_cannot_create_temp_nodes(void)
|
||||
{
|
||||
php_dom_throw_error_with_message(INVALID_MODIFICATION_ERR, "Unable to allocate temporary nodes", /* strict */ true);
|
||||
}
|
||||
|
||||
static bool dom_is_node_in_list(const zval *nodes, uint32_t nodesc, const xmlNode *node_to_find)
|
||||
{
|
||||
for (uint32_t i = 0; i < nodesc; i++) {
|
||||
@@ -353,12 +358,17 @@ xmlNode* dom_zvals_to_single_node(php_libxml_ref_obj *document, xmlNode *context
|
||||
return dom_object_get_node(Z_DOMOBJ_P(nodes));
|
||||
} else {
|
||||
ZEND_ASSERT(Z_TYPE_P(nodes) == IS_STRING);
|
||||
return xmlNewDocTextLen(documentNode, BAD_CAST Z_STRVAL_P(nodes), Z_STRLEN_P(nodes));
|
||||
node = xmlNewDocTextLen(documentNode, BAD_CAST Z_STRVAL_P(nodes), Z_STRLEN_P(nodes));
|
||||
if (UNEXPECTED(node == NULL)) {
|
||||
dom_cannot_create_temp_nodes();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
node = xmlNewDocFragment(documentNode);
|
||||
if (UNEXPECTED(!node)) {
|
||||
dom_cannot_create_temp_nodes();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -397,6 +407,10 @@ xmlNode* dom_zvals_to_single_node(php_libxml_ref_obj *document, xmlNode *context
|
||||
|
||||
/* Text nodes can't violate the hierarchy at this point. */
|
||||
newNode = xmlNewDocTextLen(documentNode, BAD_CAST Z_STRVAL(nodes[i]), Z_STRLEN(nodes[i]));
|
||||
if (UNEXPECTED(newNode == NULL)) {
|
||||
dom_cannot_create_temp_nodes();
|
||||
goto err;
|
||||
}
|
||||
dom_add_child_without_merging(node, newNode);
|
||||
}
|
||||
}
|
||||
@@ -423,7 +437,12 @@ static zend_result dom_sanity_check_node_list_types(zval *nodes, uint32_t nodesc
|
||||
zend_argument_type_error(i + 1, "must be of type %s|string, %s given", ZSTR_VAL(node_ce->name), zend_zval_type_name(&nodes[i]));
|
||||
return FAILURE;
|
||||
}
|
||||
} else if (type != IS_STRING) {
|
||||
} else if (type == IS_STRING) {
|
||||
if (Z_STRLEN(nodes[i]) > INT_MAX) {
|
||||
zend_argument_value_error(i + 1, "must be less than or equal to %d bytes long", INT_MAX);
|
||||
return FAILURE;
|
||||
}
|
||||
} else {
|
||||
zend_argument_type_error(i + 1, "must be of type %s|string, %s given", ZSTR_VAL(node_ce->name), zend_zval_type_name(&nodes[i]));
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
80
ext/dom/tests/parentnode_childnode_too_long_text.phpt
Normal file
80
ext/dom/tests/parentnode_childnode_too_long_text.phpt
Normal file
@@ -0,0 +1,80 @@
|
||||
--TEST--
|
||||
Passing a too long string to ChildNode or ParentNode methods causes an exception
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--INI--
|
||||
memory_limit=-1
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_INT_SIZE !== 8) die('skip Only for 64-bit');
|
||||
if (getenv('SKIP_SLOW_TESTS')) die('skip slow test');
|
||||
// Copied from file_get_contents_file_put_contents_5gb.phpt
|
||||
function get_system_memory(): int|float|false
|
||||
{
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||
// Windows-based memory check
|
||||
@exec('wmic OS get FreePhysicalMemory', $output);
|
||||
if (isset($output[1])) {
|
||||
return ((int)trim($output[1])) * 1024;
|
||||
}
|
||||
} else {
|
||||
// Unix/Linux-based memory check
|
||||
$memInfo = @file_get_contents("/proc/meminfo");
|
||||
if ($memInfo) {
|
||||
preg_match('/MemFree:\s+(\d+) kB/', $memInfo, $matches);
|
||||
return $matches[1] * 1024; // Convert to bytes
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (get_system_memory() < 4 * 1024 * 1024 * 1024) {
|
||||
die('skip Reason: Insufficient RAM (less than 4GB)');
|
||||
}
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
$dom = new DOMDocument;
|
||||
$element = $dom->appendChild($dom->createElement('root'));
|
||||
$str = str_repeat('X', 2**31 + 10);
|
||||
try {
|
||||
$element->append('x', $str);
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$element->prepend('x', $str);
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$element->after('x', $str);
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$element->before('x', $str);
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$element->replaceWith('x', $str);
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$element->replaceChildren('x', $str);
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump($dom->childNodes->count());
|
||||
var_dump($element->childNodes->count());
|
||||
?>
|
||||
--EXPECT--
|
||||
DOMElement::append(): Argument #2 must be less than or equal to 2147483647 bytes long
|
||||
DOMElement::prepend(): Argument #2 must be less than or equal to 2147483647 bytes long
|
||||
DOMElement::after(): Argument #2 must be less than or equal to 2147483647 bytes long
|
||||
DOMElement::before(): Argument #2 must be less than or equal to 2147483647 bytes long
|
||||
DOMElement::replaceWith(): Argument #2 must be less than or equal to 2147483647 bytes long
|
||||
DOMElement::replaceChildren(): Argument #2 must be less than or equal to 2147483647 bytes long
|
||||
int(1)
|
||||
int(0)
|
||||
Reference in New Issue
Block a user