From 9d8c8727ab39bcbe2754ed13b803bce6e7ff7073 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] Fix memory leak when calling xml_parse_into_struct() twice
---
ext/xml/tests/gh12254.phpt | 31 +++++++++++++++++++++++++++++++
ext/xml/xml.c | 27 +++++++++++++++++----------
2 files changed, 48 insertions(+), 10 deletions(-)
create mode 100644 ext/xml/tests/gh12254.phpt
diff --git a/ext/xml/tests/gh12254.phpt b/ext/xml/tests/gh12254.phpt
new file mode 100644
index 000000000000..0ecc6a4ecc38
--- /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 b641a0c87d73..56f81c4305b4 100644
--- a/ext/xml/xml.c
+++ b/ext/xml/xml.c
@@ -353,19 +353,24 @@ static zend_object *xml_parser_create_object(zend_class_entry *class_type) {
return &intern->std;
}
-static void xml_parser_free_obj(zend_object *object)
+static void xml_parser_free_ltags(xml_parser *parser)
{
- xml_parser *parser = xml_parser_from_obj(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);
}
+}
+
+static void xml_parser_free_obj(zend_object *object)
+{
+ xml_parser *parser = xml_parser_from_obj(object);
+
+ if (parser->parser) {
+ XML_ParserFree(parser->parser);
+ }
+ 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;