]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Refactor btf_kfunc_id_set_contains
authorIhor Solodrai <ihor.solodrai@linux.dev>
Tue, 20 Jan 2026 22:26:26 +0000 (14:26 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 21 Jan 2026 00:15:56 +0000 (16:15 -0800)
btf_kfunc_id_set_contains() is called by fetch_kfunc_meta() in the BPF
verifier to get the kfunc flags stored in the .BTF_ids ELF section.
If it returns NULL instead of a valid pointer, it's interpreted as an
illegal kfunc usage failing the verification.

There are two potential reasons for btf_kfunc_id_set_contains() to
return NULL:

  1. Provided kfunc BTF id is not present in relevant kfunc id sets.
  2. The kfunc is not allowed, as determined by the program type
     specific filter [1].

The filter functions accept a pointer to `struct bpf_prog`, so they
might implicitly depend on earlier stages of verification, when
bpf_prog members are set.

For example, bpf_qdisc_kfunc_filter() in linux/net/sched/bpf_qdisc.c
inspects prog->aux->st_ops [2], which is initialized in:

    check_attach_btf_id() -> check_struct_ops_btf_id()

So far this hasn't been an issue, because fetch_kfunc_meta() is the
only caller of btf_kfunc_id_set_contains().

However in subsequent patches of this series it is necessary to
inspect kfunc flags earlier in BPF verifier, in the add_kfunc_call().

To resolve this, refactor btf_kfunc_id_set_contains() into two
interface functions:
  * btf_kfunc_flags() that simply returns pointer to kfunc_flags
    without applying the filters
  * btf_kfunc_is_allowed() that both checks for kfunc_flags existence
    (which is a requirement for a kfunc to be allowed) and applies the
    prog filters

See [3] for the previous version of this patch.

[1] https://lore.kernel.org/all/20230519225157.760788-7-aditi.ghag@isovalent.com/
[2] https://lore.kernel.org/all/20250409214606.2000194-4-ameryhung@gmail.com/
[3] https://lore.kernel.org/bpf/20251029190113.3323406-3-ihor.solodrai@linux.dev/

Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Link: https://lore.kernel.org/r/20260120222638.3976562-2-ihor.solodrai@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/btf.h
kernel/bpf/btf.c
kernel/bpf/verifier.c

index 78dc79810c7ddb8c773591fc266c6bb535d0d022..a2f4f383f5b66b7789d324e01e9d2309a8321feb 100644 (file)
@@ -575,8 +575,8 @@ const char *btf_name_by_offset(const struct btf *btf, u32 offset);
 const char *btf_str_by_offset(const struct btf *btf, u32 offset);
 struct btf *btf_parse_vmlinux(void);
 struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
-u32 *btf_kfunc_id_set_contains(const struct btf *btf, u32 kfunc_btf_id,
-                              const struct bpf_prog *prog);
+u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
+bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
 u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
                                const struct bpf_prog *prog);
 int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
index 364dd84bfc5a2b542b30beb86eacf9dcdb36bb31..d10b3404260f354e03a01a32702c7c4b41534117 100644 (file)
@@ -8757,24 +8757,17 @@ end:
        return ret;
 }
 
-static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
-                                       enum btf_kfunc_hook hook,
-                                       u32 kfunc_btf_id,
-                                       const struct bpf_prog *prog)
+static u32 *btf_kfunc_id_set_contains(const struct btf *btf,
+                                     enum btf_kfunc_hook hook,
+                                     u32 kfunc_btf_id)
 {
-       struct btf_kfunc_hook_filter *hook_filter;
        struct btf_id_set8 *set;
-       u32 *id, i;
+       u32 *id;
 
        if (hook >= BTF_KFUNC_HOOK_MAX)
                return NULL;
        if (!btf->kfunc_set_tab)
                return NULL;
-       hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
-       for (i = 0; i < hook_filter->nr_filters; i++) {
-               if (hook_filter->filters[i](prog, kfunc_btf_id))
-                       return NULL;
-       }
        set = btf->kfunc_set_tab->sets[hook];
        if (!set)
                return NULL;
@@ -8785,6 +8778,28 @@ static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
        return id + 1;
 }
 
+static bool __btf_kfunc_is_allowed(const struct btf *btf,
+                                  enum btf_kfunc_hook hook,
+                                  u32 kfunc_btf_id,
+                                  const struct bpf_prog *prog)
+{
+       struct btf_kfunc_hook_filter *hook_filter;
+       int i;
+
+       if (hook >= BTF_KFUNC_HOOK_MAX)
+               return false;
+       if (!btf->kfunc_set_tab)
+               return false;
+
+       hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
+       for (i = 0; i < hook_filter->nr_filters; i++) {
+               if (hook_filter->filters[i](prog, kfunc_btf_id))
+                       return false;
+       }
+
+       return true;
+}
+
 static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
 {
        switch (prog_type) {
@@ -8832,6 +8847,26 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
        }
 }
 
+bool btf_kfunc_is_allowed(const struct btf *btf,
+                         u32 kfunc_btf_id,
+                         const struct bpf_prog *prog)
+{
+       enum bpf_prog_type prog_type = resolve_prog_type(prog);
+       enum btf_kfunc_hook hook;
+       u32 *kfunc_flags;
+
+       kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
+       if (kfunc_flags && __btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog))
+               return true;
+
+       hook = bpf_prog_type_to_kfunc_hook(prog_type);
+       kfunc_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
+       if (kfunc_flags && __btf_kfunc_is_allowed(btf, hook, kfunc_btf_id, prog))
+               return true;
+
+       return false;
+}
+
 /* Caution:
  * Reference to the module (obtained using btf_try_get_module) corresponding to
  * the struct btf *MUST* be held when calling this function from verifier
@@ -8839,26 +8874,27 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
  * keeping the reference for the duration of the call provides the necessary
  * protection for looking up a well-formed btf->kfunc_set_tab.
  */
-u32 *btf_kfunc_id_set_contains(const struct btf *btf,
-                              u32 kfunc_btf_id,
-                              const struct bpf_prog *prog)
+u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog)
 {
        enum bpf_prog_type prog_type = resolve_prog_type(prog);
        enum btf_kfunc_hook hook;
        u32 *kfunc_flags;
 
-       kfunc_flags = __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog);
+       kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
        if (kfunc_flags)
                return kfunc_flags;
 
        hook = bpf_prog_type_to_kfunc_hook(prog_type);
-       return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id, prog);
+       return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
 }
 
 u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
                                const struct bpf_prog *prog)
 {
-       return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog);
+       if (!__btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog))
+               return NULL;
+
+       return btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id);
 }
 
 static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,
index b67d8981b058778933af4084d8b958833ff8bff7..c344371feb3304585c705de67b2673668efe53b7 100644 (file)
@@ -13723,10 +13723,10 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
                *kfunc_name = func_name;
        func_proto = btf_type_by_id(desc_btf, func->type);
 
-       kfunc_flags = btf_kfunc_id_set_contains(desc_btf, func_id, env->prog);
-       if (!kfunc_flags) {
+       if (!btf_kfunc_is_allowed(desc_btf, func_id, env->prog))
                return -EACCES;
-       }
+
+       kfunc_flags = btf_kfunc_flags(desc_btf, func_id, env->prog);
 
        memset(meta, 0, sizeof(*meta));
        meta->btf = desc_btf;