libxml2/backport-malloc-fail-Fix-memory-leak-after-calling-xmlXPathNodeSetMerge.patch
zhuofeng feb7e8218d backport upstream patches
(cherry picked from commit ec64ed27a9add0f7a9bf6ee351ad67302a60c383)
2023-06-20 11:16:46 +08:00

234 lines
7.0 KiB
Diff

From 8d22e065888942b8c1b5be8994c6887b5a687246 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Wed, 15 Feb 2023 14:41:11 +0100
Subject: [PATCH] malloc-fail: Fix memory leak after calling
xmlXPathNodeSetMerge
Destroy the first argument in xmlXPathNodeSetMerge if the function
fails. This is somewhat dangerous but matches the expectations of users.
Found with libFuzzer, see #344.
Reference:https://github.com/GNOME/libxml2/commit/8d22e065888942b8c1b5be8994c6887b5a687246
Conflict:xpath.c
---
xpath.c | 78 +++++++++++++++++++++++++++------------------------------
1 file changed, 37 insertions(+), 41 deletions(-)
diff --git a/xpath.c b/xpath.c
index 9ead497..5a6d762 100644
--- a/xpath.c
+++ b/xpath.c
@@ -153,6 +153,9 @@
* any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT)
*/
+static void
+xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes);
+
#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
/**
* xmlXPathCmpNodesExt:
@@ -3840,6 +3843,8 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
* if @val1 is NULL, a new set is created and copied from @val2
*
* Returns @val1 once extended or NULL in case of error.
+ *
+ * Frees @val1 in case of error.
*/
xmlNodeSetPtr
xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
@@ -3849,35 +3854,8 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
if (val2 == NULL) return(val1);
if (val1 == NULL) {
val1 = xmlXPathNodeSetCreate(NULL);
- if (val1 == NULL)
- return (NULL);
-#if 0
- /*
- * TODO: The optimization won't work in every case, since
- * those nasty namespace nodes need to be added with
- * xmlXPathNodeSetDupNs() to the set; thus a pure
- * memcpy is not possible.
- * If there was a flag on the nodesetval, indicating that
- * some temporary nodes are in, that would be helpful.
- */
- /*
- * Optimization: Create an equally sized node-set
- * and memcpy the content.
- */
- val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
- if (val1 == NULL)
- return(NULL);
- if (val2->nodeNr != 0) {
- if (val2->nodeNr == 1)
- *(val1->nodeTab) = *(val2->nodeTab);
- else {
- memcpy(val1->nodeTab, val2->nodeTab,
- val2->nodeNr * sizeof(xmlNodePtr));
- }
- val1->nodeNr = val2->nodeNr;
- }
- return(val1);
-#endif
+ if (val1 == NULL)
+ return (NULL);
}
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
@@ -3916,7 +3894,7 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
sizeof(xmlNodePtr));
if (val1->nodeTab == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
+ goto error;
}
memset(val1->nodeTab, 0 ,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
@@ -3926,13 +3904,13 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
- return(NULL);
+ goto error;
}
temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
+ goto error;
}
val1->nodeTab = temp;
val1->nodeMax *= 2;
@@ -3942,13 +3920,17 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
if (nsNode == NULL)
- return(NULL);
+ goto error;
val1->nodeTab[val1->nodeNr++] = nsNode;
} else
val1->nodeTab[val1->nodeNr++] = n2;
}
return(val1);
+
+error:
+ xmlXPathFreeNodeSet(val1);
+ return(NULL);
}
@@ -3961,6 +3943,8 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
* Checks for duplicate nodes. Clears set2.
*
* Returns @set1 once extended or NULL in case of error.
+ *
+ * Frees @set1 in case of error.
*/
static xmlNodeSetPtr
xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
@@ -3989,7 +3973,6 @@ xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
/*
* Free the namespace node.
*/
- set2->nodeTab[i] = NULL;
xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
goto skip_node;
}
@@ -4003,7 +3986,7 @@ xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
if (set1->nodeTab == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
+ goto error;
}
memset(set1->nodeTab, 0,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
@@ -4013,24 +3996,29 @@ xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
- return(NULL);
+ goto error;
}
temp = (xmlNodePtr *) xmlRealloc(
set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
+ goto error;
}
set1->nodeTab = temp;
set1->nodeMax *= 2;
}
set1->nodeTab[set1->nodeNr++] = n2;
skip_node:
- {}
+ set2->nodeTab[i] = NULL;
}
}
set2->nodeNr = 0;
return(set1);
+
+error:
+ xmlXPathFreeNodeSet(set1);
+ xmlXPathNodeSetClear(set2, 1);
+ return(NULL);
}
/**
@@ -4042,6 +4030,8 @@ skip_node:
* Doesn't check for duplicate nodes. Clears set2.
*
* Returns @set1 once extended or NULL in case of error.
+ *
+ * Frees @set1 in case of error.
*/
static xmlNodeSetPtr
xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
@@ -4057,7 +4047,7 @@ xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
if (set1->nodeTab == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
+ goto error;
}
memset(set1->nodeTab, 0,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
@@ -4067,22 +4057,28 @@ xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
- return(NULL);
+ goto error;
}
temp = (xmlNodePtr *) xmlRealloc(
set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n");
- return(NULL);
+ goto error;
}
set1->nodeTab = temp;
set1->nodeMax *= 2;
}
set1->nodeTab[set1->nodeNr++] = n2;
+ set2->nodeTab[i] = NULL;
}
}
set2->nodeNr = 0;
return(set1);
+
+error:
+ xmlXPathFreeNodeSet(set1);
+ xmlXPathNodeSetClear(set2, 1);
+ return(NULL);
}
/**
--
2.27.0