From d17a223a9533b6ef353c982b0cb1c89dd43c50bb Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Sun, 8 Nov 2026 18:38:22 +0100
Subject: [PATCH v1 2/2] Reject VERSION values in xmlroot() that break the XML
 declaration

xmlroot() copied VERSION into the XML declaration without checking it.
A version containing a quote or a non-ASCII character, e.g. '1.0"x',
produced a declaration that cannot be parsed, so the value could not be
restored from a dump.

Raise an error unless the version can be read back unchanged from the
declaration that was built.  The version is not otherwise checked for
validity.
---
 src/backend/utils/adt/xml.c       | 14 +++++++++++++-
 src/test/regress/expected/xml.out | 14 ++++----------
 src/test/regress/sql/xml.sql      |  6 ++----
 3 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index b6e7e367723..1b58f56edd0 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -1133,7 +1133,19 @@ xmlroot(xmltype *data, text *version, int standalone)
 	}
 
 	initStringInfo(&buf);
-	print_xml_decl(&buf, orig_version, 0, orig_standalone);
+	if (print_xml_decl(&buf, orig_version, 0, orig_standalone) && orig_version)
+	{
+		xmlChar    *parsed_version;
+
+		/* Reject a version that the XML declaration cannot represent */
+		if (parse_xml_decl((xmlChar *) buf.data, NULL, &parsed_version,
+						   NULL, NULL) != XML_ERR_OK ||
+			xmlStrcmp(parsed_version, orig_version) != 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("invalid XML version \"%s\"",
+							(char *) orig_version)));
+	}
 	appendStringInfoString(&buf, str + len);
 
 	return stringinfo_to_xmltype(&buf);
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index 0a97c5cbe03..83b4775938c 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -448,16 +448,10 @@ SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value)
  <?xml version="1.0" standalone="yes"?><foo/>
 (1 row)
 
--- values with an XML declaration that cannot be parsed raise an error
-SELECT xmlconcat(xml '<foo/>', xmlroot(xml '<bar/>', version '1.0"x'));
-ERROR:  could not parse XML declaration in stored value
-DETAIL:  Parsing XML declaration: '?>' expected.
-SELECT xmlagg(x) FROM (VALUES (xml '<foo/>'), (xmlroot(xml '<bar/>', version '1.0"x'))) v(x);
-ERROR:  could not parse XML declaration in stored value
-DETAIL:  Parsing XML declaration: '?>' expected.
-SELECT xmlroot(xmlroot(xml '<bar/>', version '1.0"x'), version '1.0');
-ERROR:  could not parse XML declaration in stored value
-DETAIL:  Parsing XML declaration: '?>' expected.
+SELECT xmlroot(xml '<foo/>', version '1.0"x');
+ERROR:  invalid XML version "1.0"x"
+SELECT xmlroot(xml '<foo/>', version '1.0" standalone="yes');
+ERROR:  invalid XML version "1.0" standalone="yes"
 SELECT xmlroot (
   xmlelement (
     name gazonk,
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 66c2c93799e..88ddfa659ed 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -116,10 +116,8 @@ SELECT xmlroot(xmlroot(xml '<foo/>', version '1.0'), version '1.1', standalone n
 SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no);
 SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value, standalone no value);
 SELECT xmlroot('<?xml version="1.1" standalone="yes"?><foo/>', version no value);
--- values with an XML declaration that cannot be parsed raise an error
-SELECT xmlconcat(xml '<foo/>', xmlroot(xml '<bar/>', version '1.0"x'));
-SELECT xmlagg(x) FROM (VALUES (xml '<foo/>'), (xmlroot(xml '<bar/>', version '1.0"x'))) v(x);
-SELECT xmlroot(xmlroot(xml '<bar/>', version '1.0"x'), version '1.0');
+SELECT xmlroot(xml '<foo/>', version '1.0"x');
+SELECT xmlroot(xml '<foo/>', version '1.0" standalone="yes');
 
 
 SELECT xmlroot (
-- 
2.56.0

