Fix GH-18597: Heap-buffer-overflow in zend_alloc.c when assigning string with UTF-8 bytes

xmlSave() also can flush in some cases. When the encoding is not
available this can fail for short inputs, resulting in an empty string
which is interned but then wrongly tagged by RETURN_NEW_STR.
Fix this by checking the error condition and switching to RETURN_STR for
defense-in-depth.

This issue also exists on 8.3, but does not crash; however, due to the
different API usage internally I cannot easily fix it on 8.3. There it
gives a partial output.

Closes GH-18606.
This commit is contained in:
Niels Dossche 2025-05-20 20:36:37 +02:00
parent 3e0a4259a8
commit 40e667280b
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
6 changed files with 26 additions and 5 deletions

3
NEWS
View file

@ -2,6 +2,9 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.4.9
- SimpleXML:
. Fixed bug GH-18597 (Heap-buffer-overflow in zend_alloc.c when assigning
string with UTF-8 bytes). (nielsdos)
06 Jun 2025, PHP 8.4.8

View file

@ -98,7 +98,7 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
status |= xmlOutputBufferFlush(out);
status |= xmlOutputBufferClose(out);
}
(void) xmlSaveClose(ctxt);
status |= xmlSaveClose(ctxt);
xmlCharEncCloseFunc(handler);
}
if (UNEXPECTED(status < 0)) {

View file

@ -282,7 +282,7 @@ static zend_string *php_new_dom_dump_node_to_str_ex(xmlNodePtr node, int options
} else {
xmlCharEncCloseFunc(handler);
}
(void) xmlSaveClose(ctxt);
status |= xmlSaveClose(ctxt);
}
if (UNEXPECTED(status < 0)) {
@ -319,7 +319,7 @@ zend_long php_new_dom_dump_node_to_file(const char *filename, xmlDocPtr doc, xml
if (EXPECTED(ctxt != NULL)) {
status = dom_xml_serialize(ctxt, out, node, format, false, get_private_data_from_node(node));
status |= xmlOutputBufferFlush(out);
(void) xmlSaveClose(ctxt);
status |= xmlSaveClose(ctxt);
}
size_t offset = php_stream_tell(stream);

View file

@ -1519,7 +1519,7 @@ static zend_string *php_libxml_default_dump_doc_to_str(xmlDocPtr doc, int option
}
long status = xmlSaveDoc(ctxt, doc);
(void) xmlSaveClose(ctxt);
status |= xmlSaveClose(ctxt);
if (status < 0) {
smart_str_free_ex(&str, false);
return NULL;

View file

@ -1404,7 +1404,8 @@ PHP_METHOD(SimpleXMLElement, asXML)
if (!result) {
RETURN_FALSE;
} else {
RETURN_NEW_STR(result);
/* Defense-in-depth: don't use the NEW variant in case somehow an empty string gets returned */
RETURN_STR(result);
}
}
/* }}} */

View file

@ -0,0 +1,17 @@
--TEST--
GH-18597 (Heap-buffer-overflow in zend_alloc.c when assigning string with UTF-8 bytes)
--EXTENSIONS--
simplexml
--FILE--
<?php
$sx1 = new SimpleXMLElement("<root />");
$sx1->node[0] = 'node1';
$node = $sx1->node[0];
$node[0] = '<27><>c';
$sx1->asXML(); // Depends on the available system encodings whether this fails or not, point is, it should not crash
echo "Done\n";
?>
--EXPECT--
Done