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

Merge branch 'bpf: Add detection of kfuncs.'

Alexei Starovoitov says:

====================

From: Alexei Starovoitov <ast@kernel.org>

Allow BPF programs detect at load time whether particular kfunc exists.

Patch 1: Allow ld_imm64 to point to kfunc in the kernel.
Patch 2: Fix relocation of kfunc in ld_imm64 insn when kfunc is in kernel module.
Patch 3: Introduce bpf_ksym_exists() macro.
Patch 4: selftest.

NOTE: detection of kfuncs from light skeleton is not supported yet.
====================

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+41 -7
+11 -6
kernel/bpf/verifier.c
··· 15952 15952 goto err_put; 15953 15953 } 15954 15954 15955 - if (!btf_type_is_var(t)) { 15956 - verbose(env, "pseudo btf_id %d in ldimm64 isn't KIND_VAR.\n", id); 15955 + if (!btf_type_is_var(t) && !btf_type_is_func(t)) { 15956 + verbose(env, "pseudo btf_id %d in ldimm64 isn't KIND_VAR or KIND_FUNC\n", id); 15957 15957 err = -EINVAL; 15958 15958 goto err_put; 15959 15959 } ··· 15966 15966 err = -ENOENT; 15967 15967 goto err_put; 15968 15968 } 15969 + insn[0].imm = (u32)addr; 15970 + insn[1].imm = addr >> 32; 15971 + 15972 + if (btf_type_is_func(t)) { 15973 + aux->btf_var.reg_type = PTR_TO_MEM | MEM_RDONLY; 15974 + aux->btf_var.mem_size = 0; 15975 + goto check_btf; 15976 + } 15969 15977 15970 15978 datasec_id = find_btf_percpu_datasec(btf); 15971 15979 if (datasec_id > 0) { ··· 15985 15977 } 15986 15978 } 15987 15979 } 15988 - 15989 - insn[0].imm = (u32)addr; 15990 - insn[1].imm = addr >> 32; 15991 15980 15992 15981 type = t->type; 15993 15982 t = btf_type_skip_modifiers(btf, type, NULL); ··· 16013 16008 aux->btf_var.btf = btf; 16014 16009 aux->btf_var.btf_id = type; 16015 16010 } 16016 - 16011 + check_btf: 16017 16012 /* check whether we recorded this BTF (and maybe module) already */ 16018 16013 for (i = 0; i < env->used_btf_cnt; i++) { 16019 16014 if (env->used_btfs[i].btf == btf) {
+5
tools/lib/bpf/bpf_helpers.h
··· 177 177 #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted"))) 178 178 #define __kptr __attribute__((btf_type_tag("kptr"))) 179 179 180 + #define bpf_ksym_exists(sym) ({ \ 181 + _Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \ 182 + !!sym; \ 183 + }) 184 + 180 185 #ifndef ___bpf_concat 181 186 #define ___bpf_concat(a, b) a ## b 182 187 #endif
+6
tools/lib/bpf/libbpf.c
··· 7533 7533 ext->is_set = true; 7534 7534 ext->ksym.kernel_btf_id = kfunc_id; 7535 7535 ext->ksym.btf_fd_idx = mod_btf ? mod_btf->fd_array_idx : 0; 7536 + /* Also set kernel_btf_obj_fd to make sure that bpf_object__relocate_data() 7537 + * populates FD into ld_imm64 insn when it's used to point to kfunc. 7538 + * {kernel_btf_id, btf_fd_idx} -> fixup bpf_call. 7539 + * {kernel_btf_id, kernel_btf_obj_fd} -> fixup ld_imm64. 7540 + */ 7541 + ext->ksym.kernel_btf_obj_fd = mod_btf ? mod_btf->fd : 0; 7536 7542 pr_debug("extern (func ksym) '%s': resolved to kernel [%d]\n", 7537 7543 ext->name, kfunc_id); 7538 7544
+19 -1
tools/testing/selftests/bpf/progs/task_kfunc_success.c
··· 17 17 * TP_PROTO(struct task_struct *p, u64 clone_flags) 18 18 */ 19 19 20 + struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym __weak; 21 + void invalid_kfunc(void) __ksym __weak; 22 + void bpf_testmod_test_mod_kfunc(int i) __ksym __weak; 23 + 20 24 static bool is_test_kfunc_task(void) 21 25 { 22 26 int cur_pid = bpf_get_current_pid_tgid() >> 32; ··· 30 26 31 27 static int test_acquire_release(struct task_struct *task) 32 28 { 33 - struct task_struct *acquired; 29 + struct task_struct *acquired = NULL; 30 + 31 + if (!bpf_ksym_exists(bpf_task_acquire)) { 32 + err = 3; 33 + return 0; 34 + } 35 + if (!bpf_ksym_exists(bpf_testmod_test_mod_kfunc)) { 36 + err = 4; 37 + return 0; 38 + } 39 + if (bpf_ksym_exists(invalid_kfunc)) { 40 + /* the verifier's dead code elimination should remove this */ 41 + err = 5; 42 + asm volatile ("goto -1"); /* for (;;); */ 43 + } 34 44 35 45 acquired = bpf_task_acquire(task); 36 46 bpf_task_release(acquired);