From b1d9a8d3217f53a0a635f05ec0947683ac431221 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Wed, 20 Sep 2023 20:49:10 +0200
Subject: [PATCH 1/2] Fix return type of stub of xml_parse_into_struct()
Closes GH-12253.
---
NEWS | 3 +++
ext/xml/xml.stub.php | 2 +-
ext/xml/xml_arginfo.h | 4 ++--
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/NEWS b/NEWS
index 2bf226a9fd9..b2114d61c66 100644
--- a/NEWS
+++ b/NEWS
@@ -34,6 +34,9 @@ PHP NEWS
foreach). (nielsdos)
. Fixed bug #55098 (SimpleXML iteration produces infinite loop). (nielsdos)
+- XML:
+ . Fix return type of stub of xml_parse_into_struct(). (nielsdos)
+
28 Sep 2023, PHP 8.1.24
- Core:
diff --git a/ext/xml/xml.stub.php b/ext/xml/xml.stub.php
index 9ab31ced425..65478d1ed9a 100644
--- a/ext/xml/xml.stub.php
+++ b/ext/xml/xml.stub.php
@@ -44,7 +44,7 @@ function xml_parse(XMLParser $parser, string $data, bool $is_final = false): int
* @param array $values
* @param array $index
*/
-function xml_parse_into_struct(XMLParser $parser, string $data, &$values, &$index = null): int {}
+function xml_parse_into_struct(XMLParser $parser, string $data, &$values, &$index = null): int|false {}
function xml_get_error_code(XMLParser $parser): int {}
diff --git a/ext/xml/xml_arginfo.h b/ext/xml/xml_arginfo.h
index 9afd9713305..e1b00e92ea6 100644
--- a/ext/xml/xml_arginfo.h
+++ b/ext/xml/xml_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 19ecc0e3b7a82f8b992a311788919154cdb28a5d */
+ * Stub hash: 7750a602e08f8a64b28ffda0f0537d384fc34f20 */
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_xml_parser_create, 0, 0, XMLParser, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null")
@@ -46,7 +46,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xml_parse, 0, 2, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, is_final, _IS_BOOL, 0, "false")
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xml_parse_into_struct, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_xml_parse_into_struct, 0, 3, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_OBJ_INFO(0, parser, XMLParser, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_ARG_INFO(1, values)
From 30f26b587ad7c27671410cfba7abd7a8dd578f83 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Wed, 20 Sep 2023 20:51:42 +0200
Subject: [PATCH 2/2] Fix memory leak when calling xml_parse_into_struct()
twice
Closes GH-12254.
---
NEWS | 1 +
ext/xml/tests/gh12254.phpt | 31 +++++++++++++++++++++++++++++++
ext/xml/xml.c | 27 +++++++++++++++++----------
3 files changed, 49 insertions(+), 10 deletions(-)
create mode 100644 ext/xml/tests/gh12254.phpt
diff --git a/NEWS b/NEWS
index b2114d61c66..669ba9a621c 100644
--- a/NEWS
+++ b/NEWS
@@ -36,6 +36,7 @@ PHP NEWS
- XML:
. Fix return type of stub of xml_parse_into_struct(). (nielsdos)
+ . Fix memory leak when calling xml_parse_into_struct() twice. (nielsdos)
28 Sep 2023, PHP 8.1.24
diff --git a/ext/xml/tests/gh12254.phpt b/ext/xml/tests/gh12254.phpt
new file mode 100644
index 00000000000..0ecc6a4ecc3
--- /dev/null
+++ b/ext/xml/tests/gh12254.phpt
@@ -0,0 +1,31 @@
+--TEST--
+GH-12254: xml_parse_into_struct() memory leak when called twice
+--EXTENSIONS--
+xml
+--FILE--
+", $values, $tags));
+}, function ($parser, $name) {
+ echo "close\n";
+ var_dump($name);
+});
+xml_parse_into_struct($parser, "", $values, $tags);
+// Yes, this doesn't do anything but it at least shouldn't leak...
+xml_parse_into_struct($parser, "", $values, $tags);
+
+?>
+--EXPECTF--
+open
+string(9) "CONTAINER"
+array(0) {
+}
+
+Warning: xml_parse_into_struct(): Parser must not be called recursively in %s on line %d
+bool(false)
+close
+string(9) "CONTAINER"
diff --git a/ext/xml/xml.c b/ext/xml/xml.c
index b641a0c87d7..56f81c4305b 100644
--- a/ext/xml/xml.c
+++ b/ext/xml/xml.c
@@ -353,6 +353,16 @@ static zend_object *xml_parser_create_object(zend_class_entry *class_type) {
return &intern->std;
}
+static void xml_parser_free_ltags(xml_parser *parser)
+{
+ if (parser->ltags) {
+ int inx;
+ for (inx = 0; ((inx < parser->level) && (inx < XML_MAXLEVEL)); inx++)
+ efree(parser->ltags[ inx ]);
+ efree(parser->ltags);
+ }
+}
+
static void xml_parser_free_obj(zend_object *object)
{
xml_parser *parser = xml_parser_from_obj(object);
@@ -360,12 +370,7 @@ static void xml_parser_free_obj(zend_object *object)
if (parser->parser) {
XML_ParserFree(parser->parser);
}
- if (parser->ltags) {
- int inx;
- for (inx = 0; ((inx < parser->level) && (inx < XML_MAXLEVEL)); inx++)
- efree(parser->ltags[ inx ]);
- efree(parser->ltags);
- }
+ xml_parser_free_ltags(parser);
if (!Z_ISUNDEF(parser->startElementHandler)) {
zval_ptr_dtor(&parser->startElementHandler);
}
@@ -1282,6 +1287,11 @@ PHP_FUNCTION(xml_parse_into_struct)
parser = Z_XMLPARSER_P(pind);
+ if (parser->isparsing) {
+ php_error_docref(NULL, E_WARNING, "Parser must not be called recursively");
+ RETURN_FALSE;
+ }
+
if (info) {
info = zend_try_array_init(info);
if (!info) {
@@ -1301,15 +1311,12 @@ PHP_FUNCTION(xml_parse_into_struct)
}
parser->level = 0;
+ xml_parser_free_ltags(parser);
parser->ltags = safe_emalloc(XML_MAXLEVEL, sizeof(char *), 0);
XML_SetElementHandler(parser->parser, _xml_startElementHandler, _xml_endElementHandler);
XML_SetCharacterDataHandler(parser->parser, _xml_characterDataHandler);
- if (parser->isparsing) {
- php_error_docref(NULL, E_WARNING, "Parser must not be called recursively");
- RETURN_FALSE;
- }
parser->isparsing = 1;
ret = XML_Parse(parser->parser, (XML_Char*)data, data_len, 1);
parser->isparsing = 0;