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;