agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v4 1/3] Adding per backend commit and rollback counters
550+ messages / 2 participants
[nested] [flat]

* [PATCH v4 1/3] Adding per backend commit and rollback counters
@ 2025-08-04 08:14  Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Bertrand Drouvot @ 2025-08-04 08:14 UTC (permalink / raw)

It relies on the existing per backend statistics that has been added in
9aea73fc61d. The new pending counters are updated when the database ones are
flushed (to reduce the overhead of incrementing new counters).
---
 src/backend/utils/activity/pgstat_backend.c  | 42 +++++++++++++++++++-
 src/backend/utils/activity/pgstat_database.c |  7 ++++
 src/include/pgstat.h                         | 15 +++++++
 src/include/utils/pgstat_internal.h          |  3 +-
 4 files changed, 65 insertions(+), 2 deletions(-)
  74.4% src/backend/utils/activity/
   7.8% src/include/utils/
  17.7% src/include/

diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 8714a85e2d9..47ce61e5093 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -36,7 +36,7 @@
  * reported within critical sections so we use static memory in order to avoid
  * memory allocation.
  */
-static PgStat_BackendPending PendingBackendStats;
+PgStat_BackendPending PendingBackendStats;
 static bool backend_has_iostats = false;
 
 /*
@@ -47,6 +47,11 @@ static bool backend_has_iostats = false;
  */
 static WalUsage prevBackendWalUsage;
 
+/*
+ * For backend commit and rollback statistics.
+ */
+bool		backend_has_xactstats = false;
+
 /*
  * Utility routines to report I/O stats for backends, kept here to avoid
  * exposing PendingBackendStats to the outside world.
@@ -259,6 +264,34 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
 	prevBackendWalUsage = pgWalUsage;
 }
 
+/*
+ * Flush out locally pending backend transaction statistics.  Locking is managed
+ * by the caller.
+ */
+static void
+pgstat_flush_backend_entry_xact(PgStat_EntryRef *entry_ref)
+{
+	PgStatShared_Backend *shbackendent;
+
+	/*
+	 * This function can be called even if nothing at all has happened for
+	 * transaction statistics.  In this case, avoid unnecessarily modifying
+	 * the stats entry.
+	 */
+	if (!backend_has_xactstats)
+		return;
+
+	shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+
+	shbackendent->stats.xact_commit += PendingBackendStats.pending_xact_commit;
+	shbackendent->stats.xact_rollback += PendingBackendStats.pending_xact_rollback;
+
+	PendingBackendStats.pending_xact_commit = 0;
+	PendingBackendStats.pending_xact_rollback = 0;
+
+	backend_has_xactstats = false;
+}
+
 /*
  * Flush out locally pending backend statistics
  *
@@ -283,6 +316,10 @@ pgstat_flush_backend(bool nowait, bits32 flags)
 		pgstat_backend_wal_have_pending())
 		has_pending_data = true;
 
+	/* Some transaction data pending? */
+	if ((flags & PGSTAT_BACKEND_FLUSH_XACT) && backend_has_xactstats)
+		has_pending_data = true;
+
 	if (!has_pending_data)
 		return false;
 
@@ -298,6 +335,9 @@ pgstat_flush_backend(bool nowait, bits32 flags)
 	if (flags & PGSTAT_BACKEND_FLUSH_WAL)
 		pgstat_flush_backend_entry_wal(entry_ref);
 
+	if (flags & PGSTAT_BACKEND_FLUSH_XACT)
+		pgstat_flush_backend_entry_xact(entry_ref);
+
 	pgstat_unlock_entry(entry_ref);
 
 	return false;
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index b31f20d41bc..400a8c5d734 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -342,6 +342,13 @@ pgstat_update_dbstats(TimestampTz ts)
 	dbentry->blk_read_time += pgStatBlockReadTime;
 	dbentry->blk_write_time += pgStatBlockWriteTime;
 
+	/* Do the same for backend stats */
+	PendingBackendStats.pending_xact_commit += pgStatXactCommit;
+	PendingBackendStats.pending_xact_rollback += pgStatXactRollback;
+
+	backend_has_xactstats = true;
+	pgstat_report_fixed = true;
+
 	if (pgstat_should_report_connstat())
 	{
 		long		secs;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 202bd2d5ace..0fdbaf79780 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -490,6 +490,8 @@ typedef struct PgStat_Backend
 	TimestampTz stat_reset_timestamp;
 	PgStat_BktypeIO io_stats;
 	PgStat_WalCounters wal_counters;
+	PgStat_Counter xact_commit;
+	PgStat_Counter xact_rollback;
 } PgStat_Backend;
 
 /* ---------
@@ -502,6 +504,12 @@ typedef struct PgStat_BackendPending
 	 * Backend statistics store the same amount of IO data as PGSTAT_KIND_IO.
 	 */
 	PgStat_PendingIO pending_io;
+
+	/*
+	 * Transaction statistics pending flush.
+	 */
+	PgStat_Counter pending_xact_commit;
+	PgStat_Counter pending_xact_rollback;
 } PgStat_BackendPending;
 
 /*
@@ -801,6 +809,13 @@ extern PGDLLIMPORT int pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_fetch_consistency;
 
 
+/*
+ * Variables in pgstat_backend.c
+ */
+
+extern PGDLLIMPORT PgStat_BackendPending PendingBackendStats;
+extern PGDLLIMPORT bool backend_has_xactstats;
+
 /*
  * Variables in pgstat_bgwriter.c
  */
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 6cf00008f63..b97184c6539 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -616,7 +616,8 @@ extern void pgstat_archiver_snapshot_cb(void);
 /* flags for pgstat_flush_backend() */
 #define PGSTAT_BACKEND_FLUSH_IO		(1 << 0)	/* Flush I/O statistics */
 #define PGSTAT_BACKEND_FLUSH_WAL   (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL   (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_XACT   (1 << 2)	/* Flush xact statistics */
+#define PGSTAT_BACKEND_FLUSH_ALL   (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_XACT)
 
 extern bool pgstat_flush_backend(bool nowait, bits32 flags);
 extern bool pgstat_backend_flush_cb(bool nowait);
-- 
2.34.1


--jg9kqX3osLGO+8A7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Adding-XID-generation-count-per-backend.patch"



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					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;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread


end of thread, other threads:[~2026-05-25 19:12 UTC | newest]

Thread overview: 550+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2025-08-04 08:14 [PATCH v4 1/3] Adding per backend commit and rollback counters Bertrand Drouvot <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox