Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

bpf: Refactor btf_kfunc_id_set_contains

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>

authored by

Ihor Solodrai and committed by
Alexei Starovoitov
ea073d18 2e6690d4

+58 -22
+2 -2
include/linux/btf.h
··· 575 575 const char *btf_str_by_offset(const struct btf *btf, u32 offset); 576 576 struct btf *btf_parse_vmlinux(void); 577 577 struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog); 578 - u32 *btf_kfunc_id_set_contains(const struct btf *btf, u32 kfunc_btf_id, 579 - const struct bpf_prog *prog); 578 + u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); 579 + bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); 580 580 u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, 581 581 const struct bpf_prog *prog); 582 582 int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
+53 -17
kernel/bpf/btf.c
··· 8757 8757 return ret; 8758 8758 } 8759 8759 8760 - static u32 *__btf_kfunc_id_set_contains(const struct btf *btf, 8761 - enum btf_kfunc_hook hook, 8762 - u32 kfunc_btf_id, 8763 - const struct bpf_prog *prog) 8760 + static u32 *btf_kfunc_id_set_contains(const struct btf *btf, 8761 + enum btf_kfunc_hook hook, 8762 + u32 kfunc_btf_id) 8764 8763 { 8765 - struct btf_kfunc_hook_filter *hook_filter; 8766 8764 struct btf_id_set8 *set; 8767 - u32 *id, i; 8765 + u32 *id; 8768 8766 8769 8767 if (hook >= BTF_KFUNC_HOOK_MAX) 8770 8768 return NULL; 8771 8769 if (!btf->kfunc_set_tab) 8772 8770 return NULL; 8773 - hook_filter = &btf->kfunc_set_tab->hook_filters[hook]; 8774 - for (i = 0; i < hook_filter->nr_filters; i++) { 8775 - if (hook_filter->filters[i](prog, kfunc_btf_id)) 8776 - return NULL; 8777 - } 8778 8771 set = btf->kfunc_set_tab->sets[hook]; 8779 8772 if (!set) 8780 8773 return NULL; ··· 8776 8783 return NULL; 8777 8784 /* The flags for BTF ID are located next to it */ 8778 8785 return id + 1; 8786 + } 8787 + 8788 + static bool __btf_kfunc_is_allowed(const struct btf *btf, 8789 + enum btf_kfunc_hook hook, 8790 + u32 kfunc_btf_id, 8791 + const struct bpf_prog *prog) 8792 + { 8793 + struct btf_kfunc_hook_filter *hook_filter; 8794 + int i; 8795 + 8796 + if (hook >= BTF_KFUNC_HOOK_MAX) 8797 + return false; 8798 + if (!btf->kfunc_set_tab) 8799 + return false; 8800 + 8801 + hook_filter = &btf->kfunc_set_tab->hook_filters[hook]; 8802 + for (i = 0; i < hook_filter->nr_filters; i++) { 8803 + if (hook_filter->filters[i](prog, kfunc_btf_id)) 8804 + return false; 8805 + } 8806 + 8807 + return true; 8779 8808 } 8780 8809 8781 8810 static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type) ··· 8847 8832 } 8848 8833 } 8849 8834 8835 + bool btf_kfunc_is_allowed(const struct btf *btf, 8836 + u32 kfunc_btf_id, 8837 + const struct bpf_prog *prog) 8838 + { 8839 + enum bpf_prog_type prog_type = resolve_prog_type(prog); 8840 + enum btf_kfunc_hook hook; 8841 + u32 *kfunc_flags; 8842 + 8843 + kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id); 8844 + if (kfunc_flags && __btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog)) 8845 + return true; 8846 + 8847 + hook = bpf_prog_type_to_kfunc_hook(prog_type); 8848 + kfunc_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); 8849 + if (kfunc_flags && __btf_kfunc_is_allowed(btf, hook, kfunc_btf_id, prog)) 8850 + return true; 8851 + 8852 + return false; 8853 + } 8854 + 8850 8855 /* Caution: 8851 8856 * Reference to the module (obtained using btf_try_get_module) corresponding to 8852 8857 * the struct btf *MUST* be held when calling this function from verifier ··· 8874 8839 * keeping the reference for the duration of the call provides the necessary 8875 8840 * protection for looking up a well-formed btf->kfunc_set_tab. 8876 8841 */ 8877 - u32 *btf_kfunc_id_set_contains(const struct btf *btf, 8878 - u32 kfunc_btf_id, 8879 - const struct bpf_prog *prog) 8842 + u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog) 8880 8843 { 8881 8844 enum bpf_prog_type prog_type = resolve_prog_type(prog); 8882 8845 enum btf_kfunc_hook hook; 8883 8846 u32 *kfunc_flags; 8884 8847 8885 - kfunc_flags = __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog); 8848 + kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id); 8886 8849 if (kfunc_flags) 8887 8850 return kfunc_flags; 8888 8851 8889 8852 hook = bpf_prog_type_to_kfunc_hook(prog_type); 8890 - return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id, prog); 8853 + return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); 8891 8854 } 8892 8855 8893 8856 u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, 8894 8857 const struct bpf_prog *prog) 8895 8858 { 8896 - return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog); 8859 + if (!__btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog)) 8860 + return NULL; 8861 + 8862 + return btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id); 8897 8863 } 8898 8864 8899 8865 static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,
+3 -3
kernel/bpf/verifier.c
··· 13723 13723 *kfunc_name = func_name; 13724 13724 func_proto = btf_type_by_id(desc_btf, func->type); 13725 13725 13726 - kfunc_flags = btf_kfunc_id_set_contains(desc_btf, func_id, env->prog); 13727 - if (!kfunc_flags) { 13726 + if (!btf_kfunc_is_allowed(desc_btf, func_id, env->prog)) 13728 13727 return -EACCES; 13729 - } 13728 + 13729 + kfunc_flags = btf_kfunc_flags(desc_btf, func_id, env->prog); 13730 13730 13731 13731 memset(meta, 0, sizeof(*meta)); 13732 13732 meta->btf = desc_btf;