Skip to content

Commit 75cf816

Browse files
theihorKernel Patches Daemon
authored andcommitted
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 by the verifier as an illegal kfunc usage which fails the verification. Conceptually, 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 place where lookup + filter logic is applied to a kfunc id. 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 does not apply the filters, and btf_kfunc_flags_if_allowed() that does. [1] https://lore.kernel.org/all/[email protected]/ [2] https://lore.kernel.org/all/[email protected]/ Signed-off-by: Ihor Solodrai <[email protected]> Reviewed-by: Eduard Zingerman <[email protected]>
1 parent df0147a commit 75cf816

File tree

3 files changed

+58
-20
lines changed

3 files changed

+58
-20
lines changed

include/linux/btf.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,8 +575,10 @@ const char *btf_name_by_offset(const struct btf *btf, u32 offset);
575575
const char *btf_str_by_offset(const struct btf *btf, u32 offset);
576576
struct btf *btf_parse_vmlinux(void);
577577
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+
u32 *btf_kfunc_flags_if_allowed(const struct btf *btf,
580+
u32 kfunc_btf_id,
581+
const struct bpf_prog *prog);
580582
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
581583
const struct bpf_prog *prog);
582584
int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,

kernel/bpf/btf.c

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8640,24 +8640,17 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook,
86408640
return ret;
86418641
}
86428642

8643-
static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
8644-
enum btf_kfunc_hook hook,
8645-
u32 kfunc_btf_id,
8646-
const struct bpf_prog *prog)
8643+
static u32 *btf_kfunc_id_set_contains(const struct btf *btf,
8644+
enum btf_kfunc_hook hook,
8645+
u32 kfunc_btf_id)
86478646
{
8648-
struct btf_kfunc_hook_filter *hook_filter;
86498647
struct btf_id_set8 *set;
8650-
u32 *id, i;
8648+
u32 *id;
86518649

86528650
if (hook >= BTF_KFUNC_HOOK_MAX)
86538651
return NULL;
86548652
if (!btf->kfunc_set_tab)
86558653
return NULL;
8656-
hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
8657-
for (i = 0; i < hook_filter->nr_filters; i++) {
8658-
if (hook_filter->filters[i](prog, kfunc_btf_id))
8659-
return NULL;
8660-
}
86618654
set = btf->kfunc_set_tab->sets[hook];
86628655
if (!set)
86638656
return NULL;
@@ -8668,6 +8661,28 @@ static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
86688661
return id + 1;
86698662
}
86708663

8664+
static bool btf_kfunc_is_allowed(const struct btf *btf,
8665+
enum btf_kfunc_hook hook,
8666+
u32 kfunc_btf_id,
8667+
const struct bpf_prog *prog)
8668+
{
8669+
struct btf_kfunc_hook_filter *hook_filter;
8670+
int i;
8671+
8672+
if (hook >= BTF_KFUNC_HOOK_MAX)
8673+
return false;
8674+
if (!btf->kfunc_set_tab)
8675+
return false;
8676+
8677+
hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
8678+
for (i = 0; i < hook_filter->nr_filters; i++) {
8679+
if (hook_filter->filters[i](prog, kfunc_btf_id))
8680+
return false;
8681+
}
8682+
8683+
return true;
8684+
}
8685+
86718686
static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
86728687
{
86738688
switch (prog_type) {
@@ -8721,26 +8736,47 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
87218736
* keeping the reference for the duration of the call provides the necessary
87228737
* protection for looking up a well-formed btf->kfunc_set_tab.
87238738
*/
8724-
u32 *btf_kfunc_id_set_contains(const struct btf *btf,
8725-
u32 kfunc_btf_id,
8726-
const struct bpf_prog *prog)
8739+
u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog)
87278740
{
87288741
enum bpf_prog_type prog_type = resolve_prog_type(prog);
87298742
enum btf_kfunc_hook hook;
87308743
u32 *kfunc_flags;
87318744

8732-
kfunc_flags = __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog);
8745+
kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
87338746
if (kfunc_flags)
87348747
return kfunc_flags;
87358748

87368749
hook = bpf_prog_type_to_kfunc_hook(prog_type);
8737-
return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id, prog);
8750+
return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
8751+
}
8752+
8753+
u32 *btf_kfunc_flags_if_allowed(const struct btf *btf,
8754+
u32 kfunc_btf_id,
8755+
const struct bpf_prog *prog)
8756+
{
8757+
enum bpf_prog_type prog_type = resolve_prog_type(prog);
8758+
enum btf_kfunc_hook hook;
8759+
u32 *kfunc_flags;
8760+
8761+
kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
8762+
if (kfunc_flags && btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog))
8763+
return kfunc_flags;
8764+
8765+
hook = bpf_prog_type_to_kfunc_hook(prog_type);
8766+
kfunc_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
8767+
if (kfunc_flags && btf_kfunc_is_allowed(btf, hook, kfunc_btf_id, prog))
8768+
return kfunc_flags;
8769+
8770+
return NULL;
87388771
}
87398772

87408773
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
87418774
const struct bpf_prog *prog)
87428775
{
8743-
return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog);
8776+
if (!btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog))
8777+
return NULL;
8778+
8779+
return btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id);
87448780
}
87458781

87468782
static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,

kernel/bpf/verifier.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13631,7 +13631,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
1363113631
*kfunc_name = func_name;
1363213632
func_proto = btf_type_by_id(desc_btf, func->type);
1363313633

13634-
kfunc_flags = btf_kfunc_id_set_contains(desc_btf, func_id, env->prog);
13634+
kfunc_flags = btf_kfunc_flags_if_allowed(desc_btf, func_id, env->prog);
1363513635
if (!kfunc_flags) {
1363613636
return -EACCES;
1363713637
}

0 commit comments

Comments
 (0)