File pacemaker-libcrmcommon-improve-patch-xpath-searches.patch of Package pacemaker.19778
commit c9e02cd1a736b017a7a79cd345382c8287ab2972
Author: Ken Gaillot <kgaillot@redhat.com>
Date: Wed Apr 4 17:38:46 2018 -0500
Low: libcrmcommon: improve patch xpath searches
The previous code unnecessarily allocated large amounts of memory,
had dead code, and potentially allowed overflow of scan buffers.
diff --git a/lib/common/xml.c b/lib/common/xml.c
index 740611226..acedfecc8 100644
--- a/lib/common/xml.c
+++ b/lib/common/xml.c
@@ -1913,38 +1913,67 @@ __first_xml_child_match(xmlNode *parent, const char *name, const char *id, int p
return NULL;
}
+/*!
+ * \internal
+ * \brief Simplified, more efficient alternative to get_xpath_object()
+ *
+ * \param[in] top Root of XML to search
+ * \param[in] key Search xpath
+ * \param[in] target_position If deleting, where to delete
+ *
+ * \return XML child matching xpath if found, NULL otherwise
+ *
+ * \note This only works on simplified xpaths found in v2 patchset diffs,
+ * i.e. the only allowed search predicate is [@id='XXX'].
+ */
static xmlNode *
__xml_find_path(xmlNode *top, const char *key, int target_position)
{
- xmlNode *target = (xmlNode*)top->doc;
- char *id = malloc(XML_BUFFER_SIZE);
- char *tag = malloc(XML_BUFFER_SIZE);
- char *section = malloc(XML_BUFFER_SIZE);
- char *current = strdup(key);
- char *remainder = malloc(XML_BUFFER_SIZE);
- int rc = 0;
+ xmlNode *target = (xmlNode*) top->doc;
+ const char *current = key;
+ char *section;
+ char *remainder;
+ char *id;
+ char *tag;
+ char *path = NULL;
+ int rc;
+ size_t key_len;
+
+ CRM_CHECK(key != NULL, return NULL);
+ key_len = strlen(key);
+
+ /* These are scanned from key after a slash, so they can't be bigger
+ * than key_len - 1 characters plus a null terminator.
+ */
- while(current) {
- rc = sscanf (current, "/%[^/]%s", section, remainder);
- if(rc <= 0) {
- crm_trace("Done");
- break;
+ remainder = calloc(key_len, sizeof(char));
+ CRM_ASSERT(remainder != NULL);
- } else if(rc > 2) {
- crm_trace("Aborting on %s", current);
- target = NULL;
- break;
+ section = calloc(key_len, sizeof(char));
+ CRM_ASSERT(section != NULL);
+
+ id = calloc(key_len, sizeof(char));
+ CRM_ASSERT(id != NULL);
+
+ tag = calloc(key_len, sizeof(char));
+ CRM_ASSERT(tag != NULL);
- } else if(tag && section) {
- int f = sscanf (section, "%[^[][@id='%[^']", tag, id);
+ do {
+ // Look for /NEXT_COMPONENT/REMAINING_COMPONENTS
+ rc = sscanf(current, "/%[^/]%s", section, remainder);
+ if (rc > 0) {
+ // Separate FIRST_COMPONENT into TAG[@id='ID']
+ int f = sscanf(section, "%[^[][@id='%[^']", tag, id);
int current_position = -1;
- /* The "target_position" is for the target tag */
- if (rc == 1 && target_position >= 0) {
+ /* The target position is for the final component tag, so only use
+ * it if there is nothing left to search after this component.
+ */
+ if ((rc == 1) && (target_position >= 0)) {
current_position = target_position;
}
- switch(f) {
+ switch (f) {
case 1:
target = __first_xml_child_match(target, tag, NULL, current_position);
break;
@@ -1952,34 +1981,25 @@ __xml_find_path(xmlNode *top, const char *key, int target_position)
target = __first_xml_child_match(target, tag, id, current_position);
break;
default:
- crm_trace("Aborting on %s", section);
+ // This should not be possible
target = NULL;
break;
}
-
- if(rc == 1 || target == NULL) {
- crm_trace("Done");
- break;
-
- } else {
- char *tmp = current;
- current = remainder;
- remainder = tmp;
- }
+ current = remainder;
}
- }
- if(target) {
- char *path = (char *)xmlGetNodePath(target);
+ // Continue if something remains to search, and we've matched so far
+ } while ((rc == 2) && target);
- crm_trace("Found %s for %s", path, key);
+ if (target) {
+ crm_trace("Found %s for %s",
+ (path = (char *) xmlGetNodePath(target)), key);
free(path);
} else {
crm_debug("No match for %s", key);
}
free(remainder);
- free(current);
free(section);
free(tag);
free(id);
@@ -2005,11 +2025,7 @@ xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
if(strcmp(op, "delete") == 0) {
crm_element_value_int(change, XML_DIFF_POSITION, &position);
}
-#if 0
- match = get_xpath_object(xpath, xml, LOG_TRACE);
-#else
match = __xml_find_path(xml, xpath, position);
-#endif
crm_trace("Performing %s on %s with %p", op, xpath, match);
if(match == NULL && strcmp(op, "delete") == 0) {