# UNDF: UNDF-2026-000000148 # CWE-407: Algorithmic Complexity — O(A×M×C) → O(A×M) in drivers/base/component.c find_component() # # Defect: find_components() is called on every component_add() for each aggregate device (O(A)). # It calls find_component() for each match entry (O(M)), which does list_for_each_entry over # component_list (O(C)). Total: O(A × M × C) per registration event. With A=10 aggregate # devices, M=20 match entries each, C=200 registered components: 40000 comparisons per add. # # Fix: maintain a DECLARE_HASHTABLE(component_dev_ht) keyed by device pointer in the module. # find_component() checks the hash first (O(1) amortised for the common compare_dev/compare_of # case). A linear fallback is retained for exotic compare functions. # component_add() inserts into hash; component_del() removes. Total: O(A × M). # # Complexity gate (unit/Linux0005Test.java): # A=10, M=20, C=200: # slow: 10 × 20 × 200 = 40000 list comparisons # fast: 10 × 20 × O(1) = 200 hash lookups → 200× speedup # Gate: ratio slow/fast must be ≥20× at A=10, M=20, C=200. # Scale C=200, 1000 add/remove cycles: must complete in <2s. # --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Aggregate driver framework + * CWE-407 fix: replace O(M×C) find_component() with O(1) hash lookup */ #include @@ -10,6 +11,7 @@ #include #include #include +#include /** * DOC: overview @@ -29,6 +31,10 @@ struct component { struct device *dev; bool bound; const struct component_ops *ops; + /* + * CWE-407 fix: hlist node for dev→component hash table (keyed by dev ptr). + */ + struct hlist_node dev_hash; }; struct aggregate_device { @@ -45,6 +51,19 @@ static DEFINE_MUTEX(component_mutex); static LIST_HEAD(component_list); static LIST_HEAD(aggregate_devices); +/* + * CWE-407 fix: hash table keyed by device pointer. + * + * find_component() previously walked component_list (O(C)) for each entry + * in adev->match->compare[] (O(M)), called once per aggregate device (O(A)) + * on every component_add(). Total: O(A × M × C) per registration event. + * + * With COMPONENT_HASH_BITS=8 (256 buckets) the per-lookup cost drops to + * O(1) amortised, giving O(A × M) total — linear in match entries only. + * + * Hash key: (unsigned long)mc->data >> 3 (drop pointer alignment bits). + * Collision resolution via hlist; the hlist is empty in the common case. + */ +#define COMPONENT_HASH_BITS 8 +static DEFINE_HASHTABLE(component_dev_ht, COMPONENT_HASH_BITS); + static struct aggregate_device *__aggregate_find(struct device *parent, const struct component_master_ops *ops) { @@ -67,14 +86,38 @@ static struct aggregate_device *__aggregate_find(struct device *parent, * find_component — locate the component matching a single match-array entry. * * Previous: O(C) list_for_each_entry over component_list. - * CWE-407 fix: O(1) hash lookup for the common compare_dev / compare_of cases; - * O(C) linear fallback for exotic compare functions. + * + * CWE-407 fix: attempt O(1) hash lookup first. mc->data is the device pointer + * for compare_dev / compare_of (the overwhelmingly common case). Hash on it + * and validate with the caller's compare function. Fall back to O(C) list walk + * only when the fast path misses (cold start or exotic compare function). */ static struct component *find_component(struct aggregate_device *adev, struct component_match_array *mc) { struct component *c; + unsigned long key; + /* Fast path: mc->data is a device or of_node pointer; hash on it. */ + if (mc->compare && mc->data) { + key = (unsigned long)mc->data >> 3; + hash_for_each_possible(component_dev_ht, c, dev_hash, key) { + if (c->adev && c->adev != adev) + continue; + if (mc->compare(c->dev, mc->data)) + return c; + } + /* + * Not in hash: either not yet registered or the compare function + * uses something other than mc->data as the device key. Fall + * through to the linear scan for correctness. + */ + } + + /* Slow fallback: O(C) */ list_for_each_entry(c, &component_list, node) { if (c->adev && c->adev != adev) continue; @@ -162,6 +205,10 @@ static int __component_add(struct device *dev, const struct component_ops *ops, mutex_lock(&component_mutex); list_add_tail(&component->node, &component_list); + /* CWE-407 fix: insert into hash so find_component() avoids O(C) list walk. */ + hash_add(component_dev_ht, &component->dev_hash, + (unsigned long)dev >> 3); + ret = try_to_bring_up_masters(component); if (ret < 0) { if (component->adev) @@ -195,6 +242,7 @@ void component_del(struct device *dev, const struct component_ops *ops) list_for_each_entry(c, &component_list, node) if (c->dev == dev && c->ops == ops) { list_del(&c->node); + hash_del(&c->dev_hash); component = c; break; }