- All 8 patches now carry required # CWE-407 / # Defect / # Fix / # Complexity gate header blocks per patch file format spec - linux-0001 (headerdep.pl): header only — code was correct - linux-0002 (auditsc.c): header + explicit break to prevent ctx->names_list fall-through - linux-0003 (dev.c): rewrote fix — skip altname loop when format has no percent-d placeholder; removes duplicate bounds check from prior draft - linux-0004 (neighbour.c): header + cleaned up ifdef guards; xarray with fallback - linux-0005 (component.c): header + existing hash fast-path retained - linux-0006 (btf.c): header + fixed cache hit path — no longer re-runs btf_find_by_name_kind on hit; uses stored btf_id directly - linux-0007 (pktgen.c): rewrote fix — replaced xa_for_each (O(N)) with dual DECLARE_HASHTABLE: dev_ht (by dev*) and name_ht (by jhash(ifname)) - linux-0008 (taskstats.c): fixed mixed list_for_each_entry/hash_for_each_possible syntax; clean replacement of duplicate-pid list scan with hash_for_each_possible - Combined patch: linux-0001..0008-hashstruct.patch (8 defects, was missing 0004) - outreach/linux.md: updated to 8 defects, corrected numbering (0001=headerdep, 0002=auditsc, 0003=dev, 0004=neighbour, 0005-0008 as before)
129 lines
4.9 KiB
Diff
129 lines
4.9 KiB
Diff
# 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 <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 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;
|
||
}
|