78 lines
3 KiB
Diff
78 lines
3 KiB
Diff
# UNDF: UNDF-2026-000000317
|
|
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
|
|
index abc1234..def5678 100644
|
|
--- a/src/feature/nodelist/nodelist.c
|
|
+++ b/src/feature/nodelist/nodelist.c
|
|
@@ -2185,6 +2185,9 @@ node_get_family_ids(const node_t *node)
|
|
/**
|
|
* Return true iff `a` and `b` have any family ID in common.
|
|
+ *
|
|
+ * NOTE: This function is O(|ids_a| * |ids_b|) via smartlist_contains_string.
|
|
+ * Callers that invoke this inside an O(N) outer loop must use the strmap-based
|
|
+ * variant below (nodes_share_family_id_set) to achieve O(N * F) overall.
|
|
**/
|
|
static bool
|
|
nodes_have_common_family_id(const node_t *a, const node_t *b)
|
|
@@ -2199,6 +2202,29 @@ nodes_have_common_family_id(const node_t *a, const node_t *b)
|
|
return false;
|
|
}
|
|
|
|
+/**
|
|
+ * Return true iff <b>node</b> has any family ID contained in <b>id_set</b>.
|
|
+ *
|
|
+ * <b>id_set</b> is a strmap_t built from another node's family IDs. Each
|
|
+ * strmap_get() is O(1), so this function is O(|node's ids|) rather than
|
|
+ * O(|node's ids| * |other node's ids|) as nodes_have_common_family_id() is.
|
|
+ *
|
|
+ * CWE-407 fix for nodelist_add_node_and_family(): build the strmap once from
|
|
+ * the fixed node's ids_a, then call this function for every node2 in the
|
|
+ * O(N) outer loop, achieving O(N * F) total instead of O(N * F^2).
|
|
+ */
|
|
+static bool
|
|
+node_has_family_id_in_set(const node_t *node, const strmap_t *id_set)
|
|
+{
|
|
+ const smartlist_t *ids = node_get_family_ids(node);
|
|
+ if (ids == NULL)
|
|
+ return false;
|
|
+ SMARTLIST_FOREACH(ids, const char *, id, {
|
|
+ if (strmap_get(id_set, id) != NULL)
|
|
+ return true;
|
|
+ });
|
|
+ return false;
|
|
+}
|
|
+
|
|
/**
|
|
* Add to <b>out</b> every node_t that is listed by <b>node</b> as being in
|
|
* its family. (Note that these nodes are not in node's family unless they
|
|
@@ -2333,10 +2359,25 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
|
|
/* Now add all the nodes that share a verified family ID with this node. */
|
|
if (use_family_ids &&
|
|
node_get_family_ids(node)) {
|
|
- SMARTLIST_FOREACH(all_nodes, const node_t *, node2, {
|
|
- if (nodes_have_common_family_id(node, node2)) {
|
|
- smartlist_add(sl, (void *)node2);
|
|
- }
|
|
- });
|
|
+ /*
|
|
+ * CWE-407 fix: build an O(1)-lookup set from node's own family IDs once,
|
|
+ * then check each node2 against it in O(|node2's ids|).
|
|
+ *
|
|
+ * Old cost: O(N * |ids_a| * |ids_b|) — smartlist_contains_string inner loop
|
|
+ * New cost: O(N * F) — strmap_get O(1) per id
|
|
+ *
|
|
+ * N = consensus size (~7 000), F = family IDs per node (typically 1-5).
|
|
+ */
|
|
+ const smartlist_t *node_ids = node_get_family_ids(node);
|
|
+ strmap_t *id_set = strmap_new();
|
|
+ SMARTLIST_FOREACH(node_ids, const char *, id, {
|
|
+ strmap_set(id_set, id, (void *)1);
|
|
+ });
|
|
+ SMARTLIST_FOREACH(all_nodes, const node_t *, node2, {
|
|
+ if (node_has_family_id_in_set(node2, id_set)) {
|
|
+ smartlist_add(sl, (void *)node2);
|
|
+ }
|
|
+ });
|
|
+ strmap_free(id_set, NULL);
|
|
}
|
|
|
|
/* If the user declared any families locally, honor those too. */
|