# UNDF: UNDF-2026-000000148 --- 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 a fixed-size hash table (COMPONENT_HASH_BITS = 8, 256 buckets) the + * per-lookup cost drops to O(1) amortised, giving O(A × M) total — linear + * in the number of match entries. + * + * NOTE: The hash key is the (dev, subcomponent) pair stored in mc->data / + * mc->compare. For the common compare_dev / compare_of cases the mc->data + * pointer IS the device (or of_node), so we can key directly on that. + */ +#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,18 +86,34 @@ static struct aggregate_device *__aggregate_find(struct device *parent, * * Previous implementation: O(C) — full list_for_each_entry over component_list. * - * CWE-407 fix: if mc->compare == component_compare_dev the match data IS - * the device pointer; use a hash-table lookup keyed on dev for O(1). - * For custom compare functions we fall back to the linear scan so - * correctness is preserved for all callers. + * CWE-407 fix: attempt O(1) hash lookup first; fall back to O(C) list walk + * only for exotic compare functions that do not compare by device pointer. */ 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 pointer (component_compare_dev) or + * an of_node pointer (component_compare_of). Hash on the raw pointer. + * We validate the match function confirms the hit before returning. + */ + if (mc->compare && mc->data) { + key = (unsigned long)mc->data >> 3; /* drop alignment bits */ + 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 found in hash — either not registered yet or compare + * uses something other than the dev pointer as 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; @@ -91,7 +126,7 @@ static struct component *find_component(struct aggregate_device *adev, return NULL; } -static int find_components(struct aggregate_device *adev) +static int find_components(struct aggregate_device *adev) /* O(M×C) → O(M) */ { struct component_match *match = adev->match; size_t i; @@ -162,6 +197,13 @@ 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 table keyed on dev pointer so + * find_component() can avoid the O(C) list walk for the common case. + */ + 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 +237,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; }