agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v27 4/9] Row pattern recognition patch (planner).
550+ messages / 2 participants
[nested] [flat]

* [PATCH v27 4/9] Row pattern recognition patch (planner).
@ 2024-12-30 23:53  Tatsuo Ishii <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Tatsuo Ishii @ 2024-12-30 23:53 UTC (permalink / raw)

---
 src/backend/optimizer/plan/createplan.c   | 22 ++++++++++++++----
 src/backend/optimizer/plan/planner.c      |  3 +++
 src/backend/optimizer/plan/setrefs.c      | 27 ++++++++++++++++++++++-
 src/backend/optimizer/prep/prepjointree.c |  9 ++++++++
 src/include/nodes/plannodes.h             | 19 ++++++++++++++++
 5 files changed, 75 insertions(+), 5 deletions(-)

diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index b3e2294e84..e99ac4652e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -291,8 +291,10 @@ static WindowAgg *make_windowagg(List *tlist, Index winref,
 								 int frameOptions, Node *startOffset, Node *endOffset,
 								 Oid startInRangeFunc, Oid endInRangeFunc,
 								 Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst,
-								 List *runCondition, List *qual, bool topWindow,
-								 Plan *lefttree);
+								 List *runCondition, RPSkipTo rpSkipTo, List *patternVariable,
+								 List *patternRegexp, List *defineClause,
+								 List *defineInitial, List *qual,
+								 bool topWindow, Plan *lefttree);
 static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
@@ -2700,6 +2702,11 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
 						  wc->inRangeAsc,
 						  wc->inRangeNullsFirst,
 						  best_path->runCondition,
+						  wc->rpSkipTo,
+						  wc->patternVariable,
+						  wc->patternRegexp,
+						  wc->defineClause,
+						  wc->defineInitial,
 						  best_path->qual,
 						  best_path->topwindow,
 						  subplan);
@@ -6708,8 +6715,10 @@ make_windowagg(List *tlist, Index winref,
 			   int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
 			   int frameOptions, Node *startOffset, Node *endOffset,
 			   Oid startInRangeFunc, Oid endInRangeFunc,
-			   Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst,
-			   List *runCondition, List *qual, bool topWindow, Plan *lefttree)
+			   Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst, List *runCondition,
+			   RPSkipTo rpSkipTo, List *patternVariable, List *patternRegexp, List *defineClause,
+			   List *defineInitial,
+			   List *qual, bool topWindow, Plan *lefttree)
 {
 	WindowAgg  *node = makeNode(WindowAgg);
 	Plan	   *plan = &node->plan;
@@ -6735,6 +6744,11 @@ make_windowagg(List *tlist, Index winref,
 	node->inRangeAsc = inRangeAsc;
 	node->inRangeNullsFirst = inRangeNullsFirst;
 	node->topWindow = topWindow;
+	node->rpSkipTo = rpSkipTo,
+		node->patternVariable = patternVariable;
+	node->patternRegexp = patternRegexp;
+	node->defineClause = defineClause;
+	node->defineInitial = defineInitial;
 
 	plan->targetlist = tlist;
 	plan->lefttree = lefttree;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7468961b01..f123721e06 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -881,6 +881,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 												EXPRKIND_LIMIT);
 		wc->endOffset = preprocess_expression(root, wc->endOffset,
 											  EXPRKIND_LIMIT);
+		wc->defineClause = (List *) preprocess_expression(root,
+														  (Node *) wc->defineClause,
+														  EXPRKIND_TARGET);
 	}
 
 	parse->limitOffset = preprocess_expression(root, parse->limitOffset,
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 6d23df108d..70c9733e3f 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -211,7 +211,6 @@ static List *set_windowagg_runcondition_references(PlannerInfo *root,
 												   List *runcondition,
 												   Plan *plan);
 
-
 /*****************************************************************************
  *
  *		SUBPLAN REFERENCES
@@ -2492,6 +2491,32 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
 					   NRM_EQUAL,
 					   NUM_EXEC_QUAL(plan));
 
+	/*
+	 * Modifies an expression tree in each DEFINE clause so that all Var
+	 * nodes's varno refers to OUTER_VAR.
+	 */
+	if (IsA(plan, WindowAgg))
+	{
+		WindowAgg  *wplan = (WindowAgg *) plan;
+
+		if (wplan->defineClause != NIL)
+		{
+			foreach(l, wplan->defineClause)
+			{
+				TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+				tle->expr = (Expr *)
+					fix_upper_expr(root,
+								   (Node *) tle->expr,
+								   subplan_itlist,
+								   OUTER_VAR,
+								   rtoffset,
+								   NRM_EQUAL,
+								   NUM_EXEC_QUAL(plan));
+			}
+		}
+	}
+
 	pfree(subplan_itlist);
 }
 
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index adad7ea9a9..842738adc9 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -2298,6 +2298,15 @@ perform_pullup_replace_vars(PlannerInfo *root,
 	parse->returningList = (List *)
 		pullup_replace_vars((Node *) parse->returningList, rvcontext);
 
+	foreach(lc, parse->windowClause)
+	{
+		WindowClause *wc = lfirst_node(WindowClause, lc);
+
+		if (wc->defineClause != NIL)
+			wc->defineClause = (List *)
+				pullup_replace_vars((Node *) wc->defineClause, rvcontext);
+	}
+
 	if (parse->onConflict)
 	{
 		parse->onConflict->onConflictSet = (List *)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 4633121689..1e7cc2a94e 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -20,6 +20,7 @@
 #include "lib/stringinfo.h"
 #include "nodes/bitmapset.h"
 #include "nodes/lockoptions.h"
+#include "nodes/parsenodes.h"
 #include "nodes/primnodes.h"
 
 
@@ -1099,6 +1100,24 @@ typedef struct WindowAgg
 	/* nulls sort first for in_range tests? */
 	bool		inRangeNullsFirst;
 
+	/* Row Pattern Recognition AFTER MACH SKIP clause */
+	RPSkipTo	rpSkipTo;		/* Row Pattern Skip To type */
+
+	/* Row Pattern PATTERN variable name (list of String) */
+	List	   *patternVariable;
+
+	/*
+	 * Row Pattern RPATTERN regular expression quantifier ('+' or ''. list of
+	 * String)
+	 */
+	List	   *patternRegexp;
+
+	/* Row Pattern DEFINE clause (list of TargetEntry) */
+	List	   *defineClause;
+
+	/* Row Pattern DEFINE variable initial names (list of String) */
+	List	   *defineInitial;
+
 	/*
 	 * false for all apart from the WindowAgg that's closest to the root of
 	 * the plan
-- 
2.25.1


----Next_Part(Tue_Dec_31_08_57_07_2024_963)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="v27-0005-Row-pattern-recognition-patch-executor.patch"



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

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

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

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

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


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



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

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

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch



^ permalink  raw  reply  [nested|flat] 550+ messages in thread

* [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list
@ 2026-05-25 19:12  Andrey Chernyy <[email protected]>
  0 siblings, 0 replies; 550+ messages in thread

From: Andrey Chernyy @ 2026-05-25 19:12 UTC (permalink / raw)

xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but
pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the
plain separator path.  Since xmlBufferWriteCHAR() copies the string
rather than taking ownership, successful xpath_list() calls leaked one
string per emitted node.

Store the cast result locally and free it with xmlFree() after writing it
to the buffer.
---
 contrib/xml2/xpath.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 7bf477e0c3f..94819961787 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 {
 	volatile xmlBufferPtr buf = NULL;
 	xmlChar    *volatile result = NULL;
+	xmlChar    *volatile str = NULL;
 	PgXmlErrorContext *xmlerrcxt;
 
 	/* spin up some error handling */
@@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 			{
 				if (plainsep != NULL)
 				{
-					xmlBufferWriteCHAR(buf,
-									   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+					str = xmlXPathCastNodeToString(nodeset->nodeTab[i]);
+					if (str == NULL || pg_xml_error_occurred(xmlerrcxt))
+						xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+									"could not allocate node text");
+
+					xmlBufferWriteCHAR(buf, str);
+					xmlFree(str);
+					str = NULL;
 
 					/* If this isn't the last entry, write the plain sep. */
 					if (i < (nodeset->nodeNr) - 1)
@@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 	}
 	PG_CATCH();
 	{
+		if (str)
+			xmlFree(str);
 		if (buf)
 			xmlBufferFree(buf);
 
-- 
2.54.0


--MP_/T7YUrG7W2jPQOBsjEYwqj9B
Content-Type: text/x-patch
Content-Transfer-Encoding: 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 --
2024-12-30 23:53 [PATCH v27 4/9] Row pattern recognition patch (planner). Tatsuo Ishii <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey Chernyy <[email protected]>
2026-05-25 19:12 [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list Andrey 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