postgres.git / summary / log / commit / refs

commit    9d33a5a804db48b254de7a0ad2fde03152f378e3
Author:   Michael Paquier <michael@paquier.xyz>
Date:     Thu Jun 11 05:29:18 2026 +0000

    xml2: Fix crash with namespace nodes in xpath_nodeset()
    
    pgxmlNodeSetToText() passed nodeTab[i]->doc to xmlNodeDump() without
    checking the node type, which could cause a crash as a
    XML_NAMESPACE_DECL maps to a xmlNs struct.  The passed-in code would
    then be dereferenced in xmlNodeDump().
    
    This commit switches the code to render XML_NAMESPACE_DECL nodes with
    xmlXPathCastNodeToString(), like xpath_table().  Some tests are added,
    written by me.
    
    Author: Andrey Chernyy <andrey.cherny@tantorlabs.com>
    Co-authored-by: Michael Paquier <michael@paquier.xyz>
    Discussion: https://postgr.es/m/20260611031436.5afde3cb@andrnote
    Backpatch-through: 14


contrib/xml2/expected/xml2.out | 8 ++++++++ contrib/xml2/expected/xml2_1.out | 8 ++++++++ contrib/xml2/sql/xml2.sql | 3 +++ contrib/xml2/xpath.c | 23 +++++++++++++++++++---- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/contrib/xml2/expected/xml2.out b/contrib/xml2/expected/xml2.out index 1906fcf33e2..9078f15f6b3 100644 --- a/contrib/xml2/expected/xml2.out +++ b/contrib/xml2/expected/xml2.out @@ -231,6 +231,14 @@ SELECT xpath_nodeset(article_xml::text, '/article/author|/article/pages', <result><item><author>test</author></item><item><pages>37</pages></item></result> (1 row) +-- namespace node +SELECT xpath_nodeset('<root xmlns:foo="http://icl.com/saxon"/>', + '//namespace::foo'); + xpath_nodeset +---------------------- + http://icl.com/saxon +(1 row) + -- xpath_list() SELECT xpath_list(article_xml::text, '/article/author|/article/pages') FROM articles; diff --git a/contrib/xml2/expected/xml2_1.out b/contrib/xml2/expected/xml2_1.out index 9a2144d58f5..62e8bd6802a 100644 --- a/contrib/xml2/expected/xml2_1.out +++ b/contrib/xml2/expected/xml2_1.out @@ -175,6 +175,14 @@ SELECT xpath_nodeset(article_xml::text, '/article/author|/article/pages', <result><item><author>test</author></item><item><pages>37</pages></item></result> (1 row) +-- namespace node +SELECT xpath_nodeset('<root xmlns:foo="http://icl.com/saxon"/>', + '//namespace::foo'); + xpath_nodeset +---------------------- + http://icl.com/saxon +(1 row) + -- xpath_list() SELECT xpath_list(article_xml::text, '/article/author|/article/pages') FROM articles; diff --git a/contrib/xml2/sql/xml2.sql b/contrib/xml2/sql/xml2.sql index 510d18a3679..145c487cbde 100644 --- a/contrib/xml2/sql/xml2.sql +++ b/contrib/xml2/sql/xml2.sql @@ -132,6 +132,9 @@ SELECT xpath_nodeset(article_xml::text, '/article/author|/article/pages', SELECT xpath_nodeset(article_xml::text, '/article/author|/article/pages', 'result', 'item') FROM articles; +-- namespace node +SELECT xpath_nodeset('<root xmlns:foo="http://icl.com/saxon"/>', + '//namespace::foo'); -- xpath_list() SELECT xpath_list(article_xml::text, '/article/author|/article/pages') diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 283bb51178d..391e39827ce 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -188,16 +188,31 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, } else { + xmlNodePtr node = nodeset->nodeTab[i]; + if ((septagname != NULL) && (xmlStrlen(septagname) > 0)) { xmlBufferWriteChar(buf, "<"); xmlBufferWriteCHAR(buf, septagname); xmlBufferWriteChar(buf, ">"); } - xmlNodeDump(buf, - nodeset->nodeTab[i]->doc, - nodeset->nodeTab[i], - 1, 0); + + /* + * XML_NAMESPACE_DECL nodes are xmlNs structs, that cannot + * be processed by xmlNodeDump(). + */ + if (node->type == XML_NAMESPACE_DECL) + { + str = xmlXPathCastNodeToString(node); + if (str == NULL || pg_xml_error_occurred(xmlerrcxt)) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate node text"); + xmlBufferWriteCHAR(buf, str); + xmlFree(str); + str = NULL; + } + else + xmlNodeDump(buf, node->doc, node, 1, 0); if ((septagname != NULL) && (xmlStrlen(septagname) > 0)) {