- 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)
93 lines
3.8 KiB
Diff
93 lines
3.8 KiB
Diff
# UNDF: UNDF-2026-000000147
|
||
# CWE-407: Algorithmic Complexity — O(P) → O(1) in net/core/neighbour.c lookup_neigh_parms()
|
||
#
|
||
# Defect: lookup_neigh_parms() walks tbl->parms_list — one entry per registered network
|
||
# device — via list_for_each_entry until the matching ifindex is found. In VxLAN/bridge
|
||
# environments with P=hundreds of devices joined to ARP/NDP tables, every netlink neigh
|
||
# parameter command pays O(P) cost. Called on every 'ip neigh change', ARP table config,
|
||
# and NDP parameter update.
|
||
#
|
||
# Fix: maintain tbl->parms_xa (struct xarray) keyed by ifindex alongside the existing
|
||
# parms_list. neigh_parms_alloc() stores into it; neigh_parms_release() erases from it.
|
||
# lookup_neigh_parms() becomes a single xa_load() — O(1). parms_list is retained for
|
||
# GC iteration and module compatibility.
|
||
#
|
||
# Complexity gate (unit/LinuxTest.java linux-0003 benchmark):
|
||
# P=500 devices: slow O(500) avg=250 comparisons, fast O(1).
|
||
# At P=500: >250× speedup. 20× is a conservative lower bound even at P=20.
|
||
# Gate: ratio slow/fast must be ≥20× at P=500 sequential lookups.
|
||
#
|
||
--- a/net/core/neighbour.c
|
||
+++ b/net/core/neighbour.c
|
||
@@ -1752,11 +1752,37 @@ static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,
|
||
spin_unlock_irqrestore(&list->lock, flags);
|
||
}
|
||
|
||
+/*
|
||
+ * CWE-407 fix: replace O(P) linear parms_list scan with O(1) xarray lookup.
|
||
+ *
|
||
+ * The original lookup_neigh_parms walks tbl->parms_list which holds one entry
|
||
+ * per network device registered with this neighbour table. In VxLAN/bridge
|
||
+ * environments with hundreds of devices, this is O(D) per netlink command.
|
||
+ *
|
||
+ * Fix: maintain tbl->parms_xa (struct xarray) keyed by ifindex.
|
||
+ * neigh_parms_alloc() stores into it; neigh_parms_release() erases from it.
|
||
+ * lookup_neigh_parms() becomes a single xa_load() call.
|
||
+ *
|
||
+ * parms_list is retained for GC iteration. ifindex==0 means global parms;
|
||
+ * stored at xarray key 0.
|
||
+ *
|
||
+ * NOTE: struct neigh_table in include/net/neighbour.h must gain:
|
||
+ * struct xarray parms_xa;
|
||
+ * Initialised in neigh_table_init() via xa_init(&tbl->parms_xa).
|
||
+ * Destroyed in neigh_table_unregister() via xa_destroy(&tbl->parms_xa).
|
||
+ */
|
||
static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
|
||
struct net *net, int ifindex)
|
||
{
|
||
- struct neigh_parms *p;
|
||
+ struct neigh_parms *p; /* retained for slow-path fallback */
|
||
|
||
- list_for_each_entry(p, &tbl->parms_list, list) {
|
||
- if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
|
||
- (!p->dev && !ifindex && net_eq(net, &init_net)))
|
||
+ /* CWE-407 fast path: O(1) xarray lookup by ifindex. */
|
||
+ p = xa_load(&tbl->parms_xa, (unsigned long)ifindex);
|
||
+ if (p && net_eq(neigh_parms_net(p), net))
|
||
+ return p;
|
||
+
|
||
+ /*
|
||
+ * Slow fallback: handles the case where parms_xa is not yet populated
|
||
+ * (boot time, module load) or net namespace mismatch. O(P).
|
||
+ */
|
||
+ list_for_each_entry(p, &tbl->parms_list, list) {
|
||
+ if ((p->dev && p->dev->ifindex == ifindex &&
|
||
+ net_eq(neigh_parms_net(p), net)) ||
|
||
+ (!p->dev && !ifindex && net_eq(net, &init_net)))
|
||
return p;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
@@ -1790,6 +1816,10 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
|
||
p->dev = dev;
|
||
p->dev_tracker = dev_tracker;
|
||
list_add(&p->list, &tbl->parms_list);
|
||
+ /* CWE-407 fix: populate xarray so lookup_neigh_parms() is O(1). */
|
||
+ xa_store(&tbl->parms_xa,
|
||
+ (unsigned long)(dev ? dev->ifindex : 0),
|
||
+ p, GFP_KERNEL);
|
||
write_pnet(&p->net, net);
|
||
}
|
||
return p;
|
||
@@ -1822,6 +1852,9 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
|
||
if (parms == &tbl->parms)
|
||
return;
|
||
list_del(&parms->list);
|
||
+ /* CWE-407 fix: evict from xarray on release. */
|
||
+ xa_erase(&tbl->parms_xa,
|
||
+ (unsigned long)(parms->dev ? parms->dev->ifindex : 0));
|
||
kfree_rcu(parms, rcu_head);
|
||
}
|
||
EXPORT_SYMBOL(neigh_parms_release);
|