java-topology/defects/linux/patch/linux-0005-component-find-quadratic.patch
russell@unturf.com 9cc2a89d0f linux: add complexity gate headers to all 8 patches; fix 0003/0007/0008 code issues
- 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)
2026-04-04 11:33:15 -04:00

129 lines
4.9 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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;
}