java-topology/defects/linux/patch/linux-0006-btf-module-scan-hash.patch

140 lines
4.3 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
--- 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,40 @@ static struct btf *btf_get_module_btf(const struct module *module);
static DEFINE_IDR(btf_idr);
static DEFINE_SPINLOCK(btf_idr_lock);
+
+/*
+ * CWE-407: bpf_find_btf_id() walked btf_idr with idr_for_each_entry() —
+ * O(M) where M = 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 comment at the call site
+ * explicitly acknowledges: "linear search could be slow".
+ *
+ * Fix: maintain a secondary hash table mapping (name_hash, kind) → btf_id
+ * for module BTFs. Built lazily on first miss; invalidated on module
+ * load/unload. Lookup drops from O(M) to O(1) amortised.
+ *
+ * Hash key: fnv1a_32(type_name) ^ kind. Collisions are resolved by a short
+ * hlist; the hlist is empty in the common case (unique type names).
+ *
+ * NOTE: This patch shows the algorithmic fix. Production wiring requires
+ * hook points in btf_alloc_id() / btf_free_id() to populate/evict entries.
+ */
+#define BTF_NAME_HASH_BITS 10 /* 1024 buckets — enough for typical module count */
+
+struct btf_name_cache_entry {
+ struct hlist_node node;
+ u32 name_hash; /* FNV-1a of type name */
+ u8 kind;
+ s32 btf_id;
+ struct btf *btf;
+};
+
+static DEFINE_HASHTABLE(btf_name_ht, BTF_NAME_HASH_BITS);
+static DEFINE_SPINLOCK(btf_name_ht_lock);
+
+static u32 btf_name_fnv1a(const char *name)
+{
+ u32 h = 2166136261u;
+ while (*name)
+ h = (h ^ (u8)*name++) * 16777619u;
+ return h;
+}
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.
+ *
* @btf_p: pointer to the found BTF object
*
* Return: btf_id if the type with @name and @kind is found,
@@ -692,6 +732,28 @@ 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: look up in name→id hash table before walking
+ * all module BTFs.
+ */
+ {
+ u32 h = btf_name_fnv1a(name) ^ kind;
+ struct btf_name_cache_entry *ce;
+
+ spin_lock_bh(&btf_name_ht_lock);
+ hash_for_each_possible(btf_name_ht, ce, node, h) {
+ if (ce->kind == kind && ce->name_hash == h &&
+ btf_find_by_name_kind(ce->btf, name, kind) == ce->btf_id) {
+ 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);
+ }
+
/* If name is not found in vmlinux's BTF then search in module's BTFs */
spin_lock_bh(&btf_idr_lock);
idr_for_each_entry(&btf_idr, btf, id) {
@@ -714,6 +756,23 @@ 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 miss so subsequent lookups for the same
+ * type are O(1). Only cache positive hits (ret > 0).
+ */
+ if (ret > 0 && *btf_p) {
+ struct btf_name_cache_entry *ce = kmalloc(sizeof(*ce), GFP_ATOMIC);
+
+ if (ce) {
+ ce->name_hash = btf_name_fnv1a(name) ^ kind;
+ ce->kind = kind;
+ ce->btf_id = ret;
+ ce->btf = *btf_p;
+ spin_lock_bh(&btf_name_ht_lock);
+ hash_add(btf_name_ht, &ce->node, ce->name_hash);
+ spin_unlock_bh(&btf_name_ht_lock);
+ }
+ }
+
return ret;
}
EXPORT_SYMBOL_GPL(bpf_find_btf_id);