mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
Merge branch 'bpf-allow-selected-kfuncs-under-bpf_spin_lock'
Kaitao Cheng says: ==================== bpf: Allow selected kfuncs under bpf_spin_lock The verifier currently has a hard-coded allowlist of kfuncs that may be called while a BPF program holds a bpf_spin_lock. This works for the small set of built-in kfuncs known to the verifier, but it does not give kfunc providers a registration-time way to declare that a kfunc is safe in such a region. In particular, module kfuncs cannot be added to that allowlist without changing verifier code. This series adds a new KF_SPINLOCK_SAFE kfunc flag and teaches the verifier to use kfunc registration metadata when deciding whether a kfunc call is allowed while a bpf_spin_lock is held. The built-in kfuncs that are currently accepted by the verifier's lock-held allowlist are annotated with the new flag. This preserves the existing behavior while removing the verifier-side category checks and uses the same mechanism for built-in and module kfuncs. The selftest coverage marks one bpf_testmod kfunc as KF_SPINLOCK_SAFE and verifies that it can be called under a bpf_spin_lock. It also calls another registered but unmarked bpf_testmod kfunc under the lock and checks that the verifier rejects it. Changes in v2: - Rename KF_SPIN_LOCK to KF_SPINLOCK_SAFE. (Kumar Kartikeya Dwivedi, Leon Hwang) - Deprecate the verifier's lock-held allowlist mechanism and annotate the relevant kfuncs uniformly with KF_SPINLOCK_SAFE (Kumar Kartikeya Dwivedi) - Add selftests. (Leon Hwang) Link to v1: https://lore.kernel.org/bpf/DKG0YUDSTBUY.1X220287HT9V3@gmail.com/ ==================== Link: https://patch.msgid.link/20260805153340.34776-1-kaitao.cheng@linux.dev Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
commit
7d008cde75
|
|
@ -79,6 +79,7 @@
|
|||
#define KF_ARENA_ARG1 (1 << 14) /* kfunc takes an arena pointer as its first argument */
|
||||
#define KF_ARENA_ARG2 (1 << 15) /* kfunc takes an arena pointer as its second argument */
|
||||
#define KF_IMPLICIT_ARGS (1 << 16) /* kfunc has implicit arguments supplied by the verifier */
|
||||
#define KF_SPINLOCK_SAFE (1 << 17) /* kfunc is allowed inside bpf_spin_lock-ed region */
|
||||
|
||||
/*
|
||||
* Tag marking a kernel function as a kfunc. This is meant to minimize the
|
||||
|
|
|
|||
|
|
@ -1118,9 +1118,9 @@ __bpf_kfunc int bpf_arena_reserve_pages(void *p__map, void *ptr__ign, u32 page_c
|
|||
__bpf_kfunc_end_defs();
|
||||
|
||||
BTF_KFUNCS_START(arena_kfuncs)
|
||||
BTF_ID_FLAGS(func, bpf_arena_alloc_pages, KF_ARENA_RET | KF_ARENA_ARG2)
|
||||
BTF_ID_FLAGS(func, bpf_arena_free_pages, KF_ARENA_ARG2)
|
||||
BTF_ID_FLAGS(func, bpf_arena_reserve_pages, KF_ARENA_ARG2)
|
||||
BTF_ID_FLAGS(func, bpf_arena_alloc_pages, KF_ARENA_RET | KF_ARENA_ARG2 | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_arena_free_pages, KF_ARENA_ARG2 | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_arena_reserve_pages, KF_ARENA_ARG2 | KF_SPINLOCK_SAFE)
|
||||
BTF_KFUNCS_END(arena_kfuncs)
|
||||
|
||||
static const struct btf_kfunc_id_set common_kfunc_set = {
|
||||
|
|
|
|||
|
|
@ -4812,30 +4812,32 @@ BTF_ID_FLAGS(func, bpf_obj_drop, KF_RELEASE | KF_IMPLICIT_ARGS)
|
|||
BTF_ID_FLAGS(func, bpf_obj_drop_impl, KF_RELEASE)
|
||||
BTF_ID_FLAGS(func, bpf_percpu_obj_drop, KF_RELEASE | KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_percpu_obj_drop_impl, KF_RELEASE)
|
||||
BTF_ID_FLAGS(func, bpf_refcount_acquire, KF_ACQUIRE | KF_RET_NULL | KF_RCU | KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_refcount_acquire_impl, KF_ACQUIRE | KF_RET_NULL | KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_front, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_front_impl)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_back, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_back_impl)
|
||||
BTF_ID_FLAGS(func, bpf_list_add, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_list_del, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_list_front, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_list_back, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_list_is_first)
|
||||
BTF_ID_FLAGS(func, bpf_list_is_last)
|
||||
BTF_ID_FLAGS(func, bpf_list_empty)
|
||||
BTF_ID_FLAGS(func, bpf_refcount_acquire,
|
||||
KF_ACQUIRE | KF_RET_NULL | KF_RCU | KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_refcount_acquire_impl,
|
||||
KF_ACQUIRE | KF_RET_NULL | KF_RCU | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_front, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_front_impl, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_back, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_back_impl, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_add, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_del, KF_ACQUIRE | KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_front, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_back, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_is_first, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_is_last, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_list_empty, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_remove, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_add, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_add_impl)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_first, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_root, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_left, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_right, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_remove, KF_ACQUIRE | KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_add, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_add_impl, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_first, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_root, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_left, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_rbtree_right, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
|
||||
#ifdef CONFIG_CGROUPS
|
||||
BTF_ID_FLAGS(func, bpf_cgroup_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
|
||||
|
|
@ -4885,9 +4887,9 @@ BTF_ID_FLAGS(func, bpf_rcu_read_lock)
|
|||
BTF_ID_FLAGS(func, bpf_rcu_read_unlock)
|
||||
BTF_ID_FLAGS(func, bpf_dynptr_slice, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_dynptr_slice_rdwr, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_iter_num_new, KF_ITER_NEW)
|
||||
BTF_ID_FLAGS(func, bpf_iter_num_next, KF_ITER_NEXT | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_iter_num_destroy, KF_ITER_DESTROY)
|
||||
BTF_ID_FLAGS(func, bpf_iter_num_new, KF_ITER_NEW | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_iter_num_next, KF_ITER_NEXT | KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_iter_num_destroy, KF_ITER_DESTROY | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_iter_task_vma_new, KF_ITER_NEW | KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_iter_task_vma_next, KF_ITER_NEXT | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_iter_task_vma_destroy, KF_ITER_DESTROY)
|
||||
|
|
@ -4962,8 +4964,8 @@ BTF_ID_FLAGS(func, bpf_strncasestr);
|
|||
#if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
|
||||
BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
|
||||
#endif
|
||||
BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_IMPLICIT_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_dynptr_from_file)
|
||||
|
|
|
|||
|
|
@ -744,10 +744,10 @@ __bpf_kfunc void bpf_res_spin_unlock_irqrestore(struct bpf_res_spin_lock *lock,
|
|||
__bpf_kfunc_end_defs();
|
||||
|
||||
BTF_KFUNCS_START(rqspinlock_kfunc_ids)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_lock, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_unlock)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_lock_irqsave, KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_unlock_irqrestore)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_lock, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_unlock, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_lock_irqsave, KF_RET_NULL | KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_res_spin_unlock_irqrestore, KF_SPINLOCK_SAFE)
|
||||
BTF_KFUNCS_END(rqspinlock_kfunc_ids)
|
||||
|
||||
static const struct btf_kfunc_id_set rqspinlock_kfunc_set = {
|
||||
|
|
|
|||
|
|
@ -11802,20 +11802,6 @@ static bool is_bpf_rbtree_api_kfunc(u32 btf_id)
|
|||
btf_id == special_kfunc_list[KF_bpf_rbtree_right];
|
||||
}
|
||||
|
||||
static bool is_bpf_iter_num_api_kfunc(u32 btf_id)
|
||||
{
|
||||
return btf_id == special_kfunc_list[KF_bpf_iter_num_new] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_iter_num_next] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_iter_num_destroy];
|
||||
}
|
||||
|
||||
static bool is_bpf_graph_api_kfunc(u32 btf_id)
|
||||
{
|
||||
return is_bpf_list_api_kfunc(btf_id) ||
|
||||
is_bpf_rbtree_api_kfunc(btf_id) ||
|
||||
is_bpf_refcount_acquire_kfunc(btf_id);
|
||||
}
|
||||
|
||||
static bool is_bpf_res_spin_lock_kfunc(u32 btf_id)
|
||||
{
|
||||
return btf_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
|
||||
|
|
@ -11824,24 +11810,16 @@ static bool is_bpf_res_spin_lock_kfunc(u32 btf_id)
|
|||
btf_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore];
|
||||
}
|
||||
|
||||
static bool is_bpf_arena_kfunc(u32 btf_id)
|
||||
static bool kfunc_spin_allowed(struct bpf_verifier_env *env, s32 func_id, s16 offset)
|
||||
{
|
||||
return btf_id == special_kfunc_list[KF_bpf_arena_alloc_pages] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_arena_free_pages] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_arena_reserve_pages];
|
||||
}
|
||||
struct bpf_kfunc_meta kfunc;
|
||||
int err;
|
||||
|
||||
static bool is_bpf_stream_kfunc(u32 btf_id)
|
||||
{
|
||||
return btf_id == special_kfunc_list[KF_bpf_stream_vprintk] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_stream_print_stack];
|
||||
}
|
||||
err = fetch_kfunc_meta(env, func_id, offset, &kfunc);
|
||||
if (err || !kfunc.flags)
|
||||
return false;
|
||||
|
||||
static bool kfunc_spin_allowed(u32 btf_id)
|
||||
{
|
||||
return is_bpf_graph_api_kfunc(btf_id) || is_bpf_iter_num_api_kfunc(btf_id) ||
|
||||
is_bpf_res_spin_lock_kfunc(btf_id) || is_bpf_arena_kfunc(btf_id) ||
|
||||
is_bpf_stream_kfunc(btf_id);
|
||||
return *kfunc.flags & KF_SPINLOCK_SAFE;
|
||||
}
|
||||
|
||||
static bool is_sync_callback_calling_kfunc(u32 btf_id)
|
||||
|
|
@ -17420,7 +17398,7 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
|
|||
insn->imm != BPF_FUNC_spin_unlock &&
|
||||
insn->imm != BPF_FUNC_kptr_xchg) ||
|
||||
(insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
|
||||
(insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) {
|
||||
!kfunc_spin_allowed(env, insn->imm, insn->off))) {
|
||||
verbose(env,
|
||||
"function calls are not allowed while holding a lock\n");
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,10 @@ static struct kfunc_test_params kfunc_tests[] = {
|
|||
TC_FAIL(kfunc_call_test_get_mem_fail_not_const, 0, "is not a const"),
|
||||
TC_FAIL(kfunc_call_test_mem_acquire_fail, 0, "acquire kernel function does not return PTR_TO_BTF_ID"),
|
||||
TC_FAIL(kfunc_call_test_pointer_arg_type_mismatch, 0, "R1 expected pointer to ctx, but got scalar"),
|
||||
TC_FAIL(kfunc_call_test_spin_lock_unsafe, 0, "function calls are not allowed while holding a lock"),
|
||||
|
||||
/* success cases */
|
||||
TC_TEST(kfunc_call_test_spin_lock_safe, 0),
|
||||
TC_TEST(kfunc_call_test1, 12),
|
||||
TC_TEST(kfunc_call_test2, 3),
|
||||
TC_TEST(kfunc_call_test4, -1234),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@
|
|||
#include <bpf/bpf_helpers.h>
|
||||
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||
|
||||
static struct bpf_spin_lock kfunc_call_lock SEC(".data.A");
|
||||
|
||||
SEC("?tc")
|
||||
int kfunc_call_test_spin_lock_unsafe(struct __sk_buff *skb)
|
||||
{
|
||||
bpf_spin_lock(&kfunc_call_lock);
|
||||
bpf_kfunc_trigger_ctx_check();
|
||||
bpf_spin_unlock(&kfunc_call_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct syscall_test_args {
|
||||
__u8 data[16];
|
||||
size_t size;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,18 @@
|
|||
#include "bpf_misc.h"
|
||||
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||
|
||||
static struct bpf_spin_lock kfunc_call_lock SEC(".data.A");
|
||||
|
||||
SEC("tc")
|
||||
int kfunc_call_test_spin_lock_safe(struct __sk_buff *skb)
|
||||
{
|
||||
bpf_spin_lock(&kfunc_call_lock);
|
||||
bpf_testmod_test_mod_kfunc(42);
|
||||
bpf_spin_unlock(&kfunc_call_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
int kfunc_call_test5(struct __sk_buff *skb)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1384,7 +1384,7 @@ __bpf_kfunc void bpf_kfunc_trigger_ctx_check(void)
|
|||
}
|
||||
|
||||
BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids)
|
||||
BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
|
||||
BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc, KF_SPINLOCK_SAFE)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test1)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test2)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test3)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user