From 589142029734f27bb92b94f0a7430e5d2d0a1205 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Mon, 2 Nov 2026 00:50:57 +0100
Subject: [PATCH v1] Fix use-after-free when XMLSERIALIZE(... INDENT) output
 exceeds 1GB

In xmltotext_with_options(), xmlSaveClose() frees the save context
unconditionally, but ctxt was only reset to NULL on the error branch.
When the close succeeded and a later step threw an error --
cstring_to_text_with_len() rejecting a result larger than MaxAllocSize
-- control reached the PG_CATCH block with ctxt still pointing at the
freed context, so xmlSaveClose() was called a second time on freed
memory and crashed the backend.

Reset ctxt to NULL immediately after xmlSaveClose(), before any code
that can throw, keeping its return value in a separate variable for the
error check.
---
 src/backend/utils/adt/xml.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 99bdbe18ee7..03301999fd4 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -733,6 +733,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 	PG_TRY();
 	{
 		size_t		decl_len = 0;
+		int			close_result;
 
 		xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
 
@@ -817,12 +818,15 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
 			xmlFreeNode(newline);
 		}
 
-		if (xmlSaveClose(ctxt) == -1 || xmlerrcxt->err_occurred)
-		{
-			ctxt = NULL;		/* don't try to close it again */
+		/*
+		 * xmlSaveClose frees ctxt whether or not it succeeds, so forget it
+		 * before anything below can throw an error.
+		 */
+		close_result = xmlSaveClose(ctxt);
+		ctxt = NULL;			/* don't try to close it again */
+		if (close_result == -1 || xmlerrcxt->err_occurred)
 			xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
 						"could not close xmlSaveCtxtPtr");
-		}
 
 		/*
 		 * xmlDocContentDumpOutput may add a trailing newline, so remove that.
-- 
2.56.0

