127 lines
4.3 KiB
Diff
127 lines
4.3 KiB
Diff
# 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 <linux/component.h>
|
||
@@ -10,6 +11,7 @@
|
||
#include <linux/mutex.h>
|
||
#include <linux/slab.h>
|
||
#include <linux/debugfs.h>
|
||
+#include <linux/hashtable.h>
|
||
|
||
/**
|
||
* 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;
|
||
}
|