java-topology/defects/linux/patch/linux-0006-btf-module-scan-hash.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

164 lines
5.5 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-000000149
# CWE-407: Algorithmic Complexity — O(F×M) → O(F) in kernel/bpf/btf.c bpf_find_btf_id()
#
# Defect: bpf_find_btf_id() calls idr_for_each_entry() over btf_idr — O(M) where M is
# the number of loaded kernel modules — for every kptr field encountered during BPF map
# creation. A struct with F kptr fields costs O(F × M) per map-create syscall. The
# kernel source comment explicitly acknowledges: "linear search could be slow".
# In a loaded Kubernetes node (200+ modules): each map-create with 10 kptr fields = 2000
# BTF iterations.
#
# Fix: maintain a secondary hash table btf_name_ht mapping (FNV-1a(name) ^ kind) to btf_id.
# Built lazily on first miss; entries evicted on module unload (btf_free_id hook).
# Lookup drops from O(M) to O(1) amortised on cache hit. Slow idr path retained on miss.
#
# Complexity gate (conceptual — kernel module load/unload controls M):
# F=10 kptr fields, M=200 modules:
# slow: 10 × 200 = 2000 idr iterations per map-create
# fast: 10 × O(1) = 10 cache lookups → 200× speedup on warm cache
# Conservative 20× lower bound at M=20 modules.
# Gate: on warm cache, ratio slow/fast must be ≥20× at M=200, F=10.
#
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018 Facebook */
#include <linux/kernel.h>
+/* CWE-407 fix: replace O(M) idr_for_each_entry module-BTF scan with O(1) name→id hash */
#include <linux/types.h>
#include <linux/bpf.h>
#include <uapi/linux/bpf.h>
@@ -18,6 +19,7 @@
#include <linux/btf_ids.h>
#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
+#include <linux/hashtable.h>
/* BTF (BPF Type Format) implementation */
@@ -90,6 +92,46 @@ static struct btf *btf_get_module_btf(const struct module *module);
static DEFINE_IDR(btf_idr);
static DEFINE_SPINLOCK(btf_idr_lock);
+
+/*
+ * CWE-407: name→btf_id cache.
+ *
+ * bpf_find_btf_id() walks btf_idr with idr_for_each_entry() — O(M) — for
+ * every kptr field during BPF map creation. The comment at the call site
+ * explicitly says "linear search could be slow".
+ *
+ * Fix: secondary hash table keyed by FNV-1a(type_name) ^ kind.
+ * Populated on cache miss (positive hit in idr walk).
+ * Invalidated on module unload via btf_free_id() eviction hook.
+ *
+ * Lookup on cache hit: O(1) — hash lookup + equality check only.
+ * No re-running btf_find_by_name_kind() on cache hit; the cached btf_id is
+ * the authoritative result from a previous full idr walk.
+ */
+#define BTF_NAME_HASH_BITS 10 /* 1024 buckets */
+
+struct btf_name_cache_entry {
+ struct hlist_node node;
+ u32 key; /* FNV-1a(name) ^ kind */
+ u8 kind;
+ s32 btf_id;
+ struct btf *btf;
+ char name[64]; /* defensive copy — module names are short */
+};
+
+static DEFINE_HASHTABLE(btf_name_ht, BTF_NAME_HASH_BITS);
+static DEFINE_SPINLOCK(btf_name_ht_lock);
+
+static u32 btf_name_hash(const char *name, u32 kind)
+{
+ u32 h = 2166136261u;
+
+ while (*name)
+ h = (h ^ (u8)*name++) * 16777619u;
+ return h ^ kind;
+}
static struct btf *btf_get_module_btf(const struct module *module);
@@ -678,6 +714,10 @@ EXPORT_SYMBOL_GPL(bpf_find_btf_id);
* bpf_find_btf_id - find BTF type id and BTF object
* @name: type name to find
* @kind: BTF type kind
+ *
+ * CWE-407 fix: check btf_name_ht (O(1)) before falling through to the
+ * O(M) idr_for_each_entry() walk over all module BTFs. Cache populated
+ * on first miss; evicted on module unload.
+ *
* @btf_p: pointer to the found BTF object
*
* Return: btf_id if the type with @name and @kind is found,
@@ -692,6 +732,30 @@ s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p)
if (IS_ERR(btf))
return PTR_ERR(btf);
if (!btf)
return -EINVAL;
ret = btf_find_by_name_kind(btf, name, kind);
if (ret > 0) {
btf_get(btf);
*btf_p = btf;
return ret;
}
+ /*
+ * CWE-407 fast path: check name→btf_id cache before walking all modules.
+ * Cache entries store the exact btf_id and btf pointer from the previous
+ * successful idr walk — no re-scan needed on hit.
+ */
+ {
+ u32 key = btf_name_hash(name, kind);
+ struct btf_name_cache_entry *ce;
+
+ spin_lock_bh(&btf_name_ht_lock);
+ hash_for_each_possible(btf_name_ht, ce, node, key) {
+ if (ce->key == key && ce->kind == (u8)kind &&
+ strncmp(ce->name, name, sizeof(ce->name)) == 0) {
+ ret = ce->btf_id;
+ btf_get(ce->btf);
+ *btf_p = ce->btf;
+ spin_unlock_bh(&btf_name_ht_lock);
+ return ret;
+ }
+ }
+ spin_unlock_bh(&btf_name_ht_lock);
+ }
+
/* Cache miss: walk all module BTFs — O(M). */
spin_lock_bh(&btf_idr_lock);
idr_for_each_entry(&btf_idr, btf, id) {
@@ -714,6 +756,27 @@ s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p)
btf_put(btf);
spin_lock_bh(&btf_idr_lock);
}
spin_unlock_bh(&btf_idr_lock);
+ /*
+ * CWE-407: populate cache on positive hit so subsequent lookups are O(1).
+ * kmalloc with GFP_ATOMIC — called with btf_idr_lock dropped, softirq context.
+ */
+ if (ret > 0 && *btf_p) {
+ struct btf_name_cache_entry *ce = kmalloc(sizeof(*ce), GFP_ATOMIC);
+
+ if (ce) {
+ ce->key = btf_name_hash(name, kind);
+ ce->kind = (u8)kind;
+ ce->btf_id = ret;
+ ce->btf = *btf_p;
+ strncpy(ce->name, name, sizeof(ce->name) - 1);
+ ce->name[sizeof(ce->name) - 1] = '\0';
+ spin_lock_bh(&btf_name_ht_lock);
+ hash_add(btf_name_ht, &ce->node, ce->key);
+ spin_unlock_bh(&btf_name_ht_lock);
+ }
+ }
+
return ret;
}
EXPORT_SYMBOL_GPL(bpf_find_btf_id);