diff --git a/NEWS b/NEWS index 24acad4ddbc..99f020223a1 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,10 @@ 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) + . Fix memory leak when calling xml_parse_into_struct() twice. (nielsdos) + 14 Sep 2023, PHP 8.3.0RC2 - Core: 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 53e959b67b0..a7c6757d7a2 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -312,6 +312,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); @@ -319,12 +329,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); } @@ -1255,6 +1260,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) { @@ -1274,15 +1284,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; diff --git a/ext/xml/xml.stub.php b/ext/xml/xml.stub.php index 89385675f4b..7047814bd44 100644 --- a/ext/xml/xml.stub.php +++ b/ext/xml/xml.stub.php @@ -182,7 +182,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 abf34ac2501..48cd269fd24 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: cd199a8733c51c8bb5970f86b7797ca91e6e59c6 */ + * Stub hash: f87e295b35cd43db72a936ee5745297a45730090 */ 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)