java-topology/defects/tor/patch/tor-0002-nodelist-family-id-strmap.patch

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. */