1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-04-24 18:50:07 +03:00

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.
This commit is contained in:
Nick Wellnhofer 2023-02-15 14:41:11 +01:00
parent d31a0e8e75
commit 8d22e06588

78
xpath.c
View File

@ -145,6 +145,9 @@
* any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT) * 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 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
/** /**
* xmlXPathCmpNodesExt: * xmlXPathCmpNodesExt:
@ -3869,6 +3872,8 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
* if @val1 is NULL, a new set is created and copied from @val2 * if @val1 is NULL, a new set is created and copied from @val2
* *
* Returns @val1 once extended or NULL in case of error. * Returns @val1 once extended or NULL in case of error.
*
* Frees @val1 in case of error.
*/ */
xmlNodeSetPtr xmlNodeSetPtr
xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
@ -3878,35 +3883,8 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
if (val2 == NULL) return(val1); if (val2 == NULL) return(val1);
if (val1 == NULL) { if (val1 == NULL) {
val1 = xmlXPathNodeSetCreate(NULL); val1 = xmlXPathNodeSetCreate(NULL);
if (val1 == NULL) if (val1 == NULL)
return (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
} }
/* @@ with_ns to check whether namespace nodes should be looked at @@ */ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
@ -3945,7 +3923,7 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
sizeof(xmlNodePtr)); sizeof(xmlNodePtr));
if (val1->nodeTab == NULL) { if (val1->nodeTab == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n"); xmlXPathErrMemory(NULL, "merging nodeset\n");
return(NULL); goto error;
} }
memset(val1->nodeTab, 0 , memset(val1->nodeTab, 0 ,
XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
@ -3955,13 +3933,13 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) { if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
xmlXPathErrMemory(NULL, "merging nodeset hit limit\n"); xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
return(NULL); goto error;
} }
temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 * temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
sizeof(xmlNodePtr)); sizeof(xmlNodePtr));
if (temp == NULL) { if (temp == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n"); xmlXPathErrMemory(NULL, "merging nodeset\n");
return(NULL); goto error;
} }
val1->nodeTab = temp; val1->nodeTab = temp;
val1->nodeMax *= 2; val1->nodeMax *= 2;
@ -3971,13 +3949,17 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
if (nsNode == NULL) if (nsNode == NULL)
return(NULL); goto error;
val1->nodeTab[val1->nodeNr++] = nsNode; val1->nodeTab[val1->nodeNr++] = nsNode;
} else } else
val1->nodeTab[val1->nodeNr++] = n2; val1->nodeTab[val1->nodeNr++] = n2;
} }
return(val1); return(val1);
error:
xmlXPathFreeNodeSet(val1);
return(NULL);
} }
@ -3990,6 +3972,8 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
* Checks for duplicate nodes. Clears set2. * Checks for duplicate nodes. Clears set2.
* *
* Returns @set1 once extended or NULL in case of error. * Returns @set1 once extended or NULL in case of error.
*
* Frees @set1 in case of error.
*/ */
static xmlNodeSetPtr static xmlNodeSetPtr
xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2) xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
@ -4018,7 +4002,6 @@ xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
/* /*
* Free the namespace node. * Free the namespace node.
*/ */
set2->nodeTab[i] = NULL;
xmlXPathNodeSetFreeNs((xmlNsPtr) n2); xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
goto skip_node; goto skip_node;
} }
@ -4032,7 +4015,7 @@ xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
if (set1->nodeTab == NULL) { if (set1->nodeTab == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n"); xmlXPathErrMemory(NULL, "merging nodeset\n");
return(NULL); goto error;
} }
memset(set1->nodeTab, 0, memset(set1->nodeTab, 0,
XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
@ -4042,24 +4025,29 @@ xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) { if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
xmlXPathErrMemory(NULL, "merging nodeset hit limit\n"); xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
return(NULL); goto error;
} }
temp = (xmlNodePtr *) xmlRealloc( temp = (xmlNodePtr *) xmlRealloc(
set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr)); set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
if (temp == NULL) { if (temp == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n"); xmlXPathErrMemory(NULL, "merging nodeset\n");
return(NULL); goto error;
} }
set1->nodeTab = temp; set1->nodeTab = temp;
set1->nodeMax *= 2; set1->nodeMax *= 2;
} }
set1->nodeTab[set1->nodeNr++] = n2; set1->nodeTab[set1->nodeNr++] = n2;
skip_node: skip_node:
{} set2->nodeTab[i] = NULL;
} }
} }
set2->nodeNr = 0; set2->nodeNr = 0;
return(set1); return(set1);
error:
xmlXPathFreeNodeSet(set1);
xmlXPathNodeSetClear(set2, 1);
return(NULL);
} }
/** /**
@ -4071,6 +4059,8 @@ skip_node:
* Doesn't check for duplicate nodes. Clears set2. * Doesn't check for duplicate nodes. Clears set2.
* *
* Returns @set1 once extended or NULL in case of error. * Returns @set1 once extended or NULL in case of error.
*
* Frees @set1 in case of error.
*/ */
static xmlNodeSetPtr static xmlNodeSetPtr
xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2) xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
@ -4086,7 +4076,7 @@ xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
if (set1->nodeTab == NULL) { if (set1->nodeTab == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n"); xmlXPathErrMemory(NULL, "merging nodeset\n");
return(NULL); goto error;
} }
memset(set1->nodeTab, 0, memset(set1->nodeTab, 0,
XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
@ -4096,22 +4086,28 @@ xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) { if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
xmlXPathErrMemory(NULL, "merging nodeset hit limit\n"); xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
return(NULL); goto error;
} }
temp = (xmlNodePtr *) xmlRealloc( temp = (xmlNodePtr *) xmlRealloc(
set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr)); set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
if (temp == NULL) { if (temp == NULL) {
xmlXPathErrMemory(NULL, "merging nodeset\n"); xmlXPathErrMemory(NULL, "merging nodeset\n");
return(NULL); goto error;
} }
set1->nodeTab = temp; set1->nodeTab = temp;
set1->nodeMax *= 2; set1->nodeMax *= 2;
} }
set1->nodeTab[set1->nodeNr++] = n2; set1->nodeTab[set1->nodeNr++] = n2;
set2->nodeTab[i] = NULL;
} }
} }
set2->nodeNr = 0; set2->nodeNr = 0;
return(set1); return(set1);
error:
xmlXPathFreeNodeSet(set1);
xmlXPathNodeSetClear(set2, 1);
return(NULL);
} }
/** /**