mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
bpf: Generate kfunc argument prototype at add-call time
Kfunc argument checking re-derives each argument's kfunc_ptr_arg_type from BTF on every verification of a call in check_kfunc_args(). Now that get_kfunc_arg_type() is a function of the kfunc's BTF alone, it no longer inspects register state. The classification can be computed once when the call is added and cached. This is a step toward describing kfuncs with a bpf_func_proto and sharing the helper argument-checking path. Generate the classification at bpf_add_kfunc_call() time: - Extend struct bpf_func_proto to be able to describe a kfunc: widen arg_type[] and the arg_btf_id[]/arg_size[] union from 5 to MAX_BPF_FUNC_ARGS, since a kfunc may take up to 12 arguments (5 in registers, 7 on the stack). - Embed a bpf_func_proto in struct bpf_kfunc_desc, populated by gen_kfunc_arg_proto() which runs get_kfunc_arg_type() for each argument and stores the result in proto.arg_type[]. Grow the descriptor table's descs[] as a flexible array to not waste memory. - check_kfunc_args() reads the cached classification from meta->fn The KF_ARG_PTR_TO_CTX classification depends on the resolved program type, and for BPF_PROG_TYPE_EXT that is the target program's type, which resolve_prog_type() reads from prog->aux->saved_dst_prog_type. That field is normally recorded later during verification in check_attach_btf_id(), after bpf_add_kfunc_call() has run. Record saved_dst_prog_type and saved_dst_attach_type from dst_prog at program load time in bpf_prog_load() so the resolved type is available at add-call time without reordering check_attach_btf_id(). This keeps e.g. an freplace of an XDP program calling bpf_xdp_metadata_rx_hash() classifying its struct xdp_md * argument as context. The classification result is unchanged; it is only computed earlier and cached. Signed-off-by: Amery Hung <ameryhung@gmail.com> Link: https://lore.kernel.org/bpf/20260801074633.1595644-19-ameryhung@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
committed by
Kumar Kartikeya Dwivedi
parent
1690dcf27c
commit
a49b70400b
@@ -960,6 +960,21 @@ enum bpf_return_type {
|
||||
};
|
||||
static_assert(__BPF_RET_TYPE_MAX <= BPF_BASE_TYPE_LIMIT);
|
||||
|
||||
/* The longest tracepoint has 12 args.
|
||||
* See include/trace/bpf_probe.h
|
||||
*
|
||||
* Also reuse this macro for maximum number of arguments a BPF function
|
||||
* or a kfunc can have. Args 1-5 are passed in registers, args 6-12 via
|
||||
* stack arg slots. The JIT may map some stack arg slots to registers based
|
||||
* on the native calling convention (e.g., arg 6 to R9 on x86-64).
|
||||
*/
|
||||
#define MAX_BPF_FUNC_ARGS 12
|
||||
|
||||
/* The maximum number of arguments passed through registers
|
||||
* a single function may have.
|
||||
*/
|
||||
#define MAX_BPF_FUNC_REG_ARGS 5
|
||||
|
||||
/* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF programs
|
||||
* to in-kernel helper functions and for adjusting imm32 field in BPF_CALL
|
||||
* instructions after verifying
|
||||
@@ -984,7 +999,7 @@ struct bpf_func_proto {
|
||||
enum bpf_arg_type arg4_type;
|
||||
enum bpf_arg_type arg5_type;
|
||||
};
|
||||
enum bpf_arg_type arg_type[5];
|
||||
enum bpf_arg_type arg_type[MAX_BPF_FUNC_ARGS];
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
@@ -994,7 +1009,7 @@ struct bpf_func_proto {
|
||||
u32 *arg4_btf_id;
|
||||
u32 *arg5_btf_id;
|
||||
};
|
||||
u32 *arg_btf_id[5];
|
||||
u32 *arg_btf_id[MAX_BPF_FUNC_ARGS];
|
||||
struct {
|
||||
size_t arg1_size;
|
||||
size_t arg2_size;
|
||||
@@ -1002,7 +1017,7 @@ struct bpf_func_proto {
|
||||
size_t arg4_size;
|
||||
size_t arg5_size;
|
||||
};
|
||||
size_t arg_size[5];
|
||||
size_t arg_size[MAX_BPF_FUNC_ARGS];
|
||||
};
|
||||
int *ret_btf_id; /* return value btf_id */
|
||||
bool (*allowed)(const struct bpf_prog *prog);
|
||||
@@ -1192,21 +1207,6 @@ struct bpf_prog_offload {
|
||||
u32 jited_len;
|
||||
};
|
||||
|
||||
/* The longest tracepoint has 12 args.
|
||||
* See include/trace/bpf_probe.h
|
||||
*
|
||||
* Also reuse this macro for maximum number of arguments a BPF function
|
||||
* or a kfunc can have. Args 1-5 are passed in registers, args 6-12 via
|
||||
* stack arg slots. The JIT may map some stack arg slots to registers based
|
||||
* on the native calling convention (e.g., arg 6 to R9 on x86-64).
|
||||
*/
|
||||
#define MAX_BPF_FUNC_ARGS 12
|
||||
|
||||
/* The maximum number of arguments passed through registers
|
||||
* a single function may have.
|
||||
*/
|
||||
#define MAX_BPF_FUNC_REG_ARGS 5
|
||||
|
||||
/* The argument is a structure or a union. */
|
||||
#define BTF_FMODEL_STRUCT_ARG BIT(0)
|
||||
|
||||
|
||||
@@ -1302,7 +1302,6 @@ static inline u32 type_flag(u32 type)
|
||||
return type & ~BPF_BASE_TYPE_MASK;
|
||||
}
|
||||
|
||||
/* only use after check_attach_btf_id() */
|
||||
static inline enum bpf_prog_type resolve_prog_type(const struct bpf_prog *prog)
|
||||
{
|
||||
return (prog->type == BPF_PROG_TYPE_EXT && prog->aux->saved_dst_prog_type) ?
|
||||
@@ -1489,6 +1488,7 @@ struct bpf_call_arg_meta {
|
||||
/* Common */
|
||||
struct btf *btf;
|
||||
u32 func_id;
|
||||
const struct bpf_func_proto *fn;
|
||||
u8 release_regno;
|
||||
u32 ret_btf_id;
|
||||
u32 subprogno;
|
||||
@@ -1617,6 +1617,7 @@ enum bpf_reg_arg_type {
|
||||
|
||||
struct bpf_kfunc_desc {
|
||||
struct btf_func_model func_model;
|
||||
struct bpf_func_proto proto;
|
||||
u32 func_id;
|
||||
s32 imm;
|
||||
u16 offset;
|
||||
@@ -1624,13 +1625,15 @@ struct bpf_kfunc_desc {
|
||||
};
|
||||
|
||||
struct bpf_kfunc_desc_tab {
|
||||
u32 nr_descs;
|
||||
/* Sorted by func_id (BTF ID) and offset (fd_array offset) during
|
||||
* verification. JITs do lookups by bpf_insn, where func_id may not be
|
||||
* available, therefore at the end of verification do_misc_fixups()
|
||||
* sorts this by imm and offset.
|
||||
*
|
||||
* Grown one entry at a time by bpf_add_kfunc_call().
|
||||
*/
|
||||
struct bpf_kfunc_desc descs[MAX_KFUNC_DESCS];
|
||||
u32 nr_descs;
|
||||
struct bpf_kfunc_desc descs[];
|
||||
};
|
||||
|
||||
/* Functions exported from verifier.c, used by fixups.c */
|
||||
|
||||
@@ -3043,6 +3043,10 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
|
||||
prog->aux->attach_btf = attach_btf;
|
||||
prog->aux->attach_btf_id = multi_func ? bpf_multi_func_btf_id[0] : attr->attach_btf_id;
|
||||
prog->aux->dst_prog = dst_prog;
|
||||
if (dst_prog) {
|
||||
prog->aux->saved_dst_prog_type = dst_prog->type;
|
||||
prog->aux->saved_dst_attach_type = dst_prog->expected_attach_type;
|
||||
}
|
||||
prog->aux->dev_bound = !!attr->prog_ifindex;
|
||||
prog->aux->xdp_has_frags = attr->prog_flags & BPF_F_XDP_HAS_FRAGS;
|
||||
|
||||
|
||||
@@ -2721,8 +2721,12 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
|
||||
struct bpf_func_proto *proto);
|
||||
|
||||
int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
|
||||
{
|
||||
struct bpf_call_arg_meta meta;
|
||||
struct bpf_kfunc_btf_tab *btf_tab;
|
||||
struct btf_func_model func_model;
|
||||
struct bpf_kfunc_desc_tab *tab;
|
||||
@@ -2808,11 +2812,30 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
desc = &tab->descs[tab->nr_descs++];
|
||||
memset(&meta, 0, sizeof(meta));
|
||||
meta.btf = kfunc.btf;
|
||||
meta.func_id = kfunc.id;
|
||||
meta.func_proto = kfunc.proto;
|
||||
meta.func_name = kfunc.name;
|
||||
meta.kfunc_flags = kfunc.flags ? *kfunc.flags : 0;
|
||||
|
||||
tab = krealloc(tab, struct_size(tab, descs, tab->nr_descs + 1), GFP_KERNEL_ACCOUNT);
|
||||
if (!tab)
|
||||
return -ENOMEM;
|
||||
prog_aux->kfunc_tab = tab;
|
||||
|
||||
desc = &tab->descs[tab->nr_descs];
|
||||
memset(desc, 0, sizeof(*desc));
|
||||
|
||||
err = gen_kfunc_arg_proto(env, &meta, &desc->proto);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
desc->func_id = func_id;
|
||||
desc->offset = offset;
|
||||
desc->addr = addr;
|
||||
desc->func_model = func_model;
|
||||
tab->nr_descs++;
|
||||
sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
|
||||
kfunc_desc_cmp_by_id_off, NULL);
|
||||
return 0;
|
||||
@@ -8332,9 +8355,9 @@ static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_stat
|
||||
|
||||
static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
|
||||
struct bpf_call_arg_meta *meta,
|
||||
const struct bpf_func_proto *fn,
|
||||
int insn_idx)
|
||||
{
|
||||
const struct bpf_func_proto *fn = meta->fn;
|
||||
u32 regno = BPF_REG_1 + arg;
|
||||
struct bpf_reg_state *reg = reg_state(env, regno);
|
||||
enum bpf_arg_type arg_type = fn->arg_type[arg];
|
||||
@@ -8847,6 +8870,8 @@ static bool check_raw_mode_ok(const struct bpf_func_proto *fn, struct bpf_call_a
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
|
||||
if (fn->arg_type[i] == ARG_DONTCARE)
|
||||
break;
|
||||
if (!arg_type_is_raw_mem(fn->arg_type[i]))
|
||||
continue;
|
||||
if (meta->arg_raw_mem.regno)
|
||||
@@ -8895,6 +8920,8 @@ static bool check_btf_id_ok(const struct bpf_func_proto *fn)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
|
||||
if (fn->arg_type[i] == ARG_DONTCARE)
|
||||
break;
|
||||
if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID)
|
||||
return !!fn->arg_btf_id[i];
|
||||
if (base_type(fn->arg_type[i]) == ARG_PTR_TO_SPIN_LOCK)
|
||||
@@ -8916,6 +8943,8 @@ static bool check_mem_arg_rw_flag_ok(const struct bpf_func_proto *fn)
|
||||
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
|
||||
enum bpf_arg_type arg_type = fn->arg_type[i];
|
||||
|
||||
if (arg_type == ARG_DONTCARE)
|
||||
break;
|
||||
if (base_type(arg_type) != ARG_PTR_TO_MEM)
|
||||
continue;
|
||||
if (!(arg_type & (MEM_WRITE | MEM_RDONLY)))
|
||||
@@ -8932,6 +8961,8 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
|
||||
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
|
||||
enum bpf_arg_type arg_type = fn->arg_type[i];
|
||||
|
||||
if (arg_type == ARG_DONTCARE)
|
||||
break;
|
||||
if (arg_type_is_release(arg_type)) {
|
||||
if (meta->release_regno)
|
||||
return false;
|
||||
@@ -10321,9 +10352,10 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
||||
env->insn_aux_data[insn_idx].non_sleepable = true;
|
||||
|
||||
meta.func_id = func_id;
|
||||
meta.fn = fn;
|
||||
/* check args */
|
||||
for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
|
||||
err = check_func_arg(env, i, &meta, fn, insn_idx);
|
||||
err = check_func_arg(env, i, &meta, insn_idx);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@@ -11463,6 +11495,43 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
|
||||
return arg_type;
|
||||
}
|
||||
|
||||
static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
|
||||
struct bpf_func_proto *proto)
|
||||
{
|
||||
const struct btf *btf = meta->btf;
|
||||
const struct btf_param *args;
|
||||
u32 i, nargs;
|
||||
int arg_type;
|
||||
|
||||
args = (const struct btf_param *)(meta->func_proto + 1);
|
||||
nargs = btf_type_vlen(meta->func_proto);
|
||||
if (nargs > MAX_BPF_FUNC_ARGS) {
|
||||
verbose(env, "Function %s has %d > %d args\n", meta->func_name,
|
||||
nargs, MAX_BPF_FUNC_ARGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (nargs > MAX_BPF_FUNC_REG_ARGS && !bpf_jit_supports_stack_args()) {
|
||||
verbose(env, "JIT does not support kfunc %s() with %d args\n",
|
||||
meta->func_name, nargs);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
for (i = 0; i < nargs; i++) {
|
||||
if (is_kfunc_arg_prog_aux(btf, &args[i]) ||
|
||||
is_kfunc_arg_ignore(btf, &args[i]) ||
|
||||
is_kfunc_arg_implicit(meta, i))
|
||||
continue;
|
||||
|
||||
arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
|
||||
if (arg_type < 0)
|
||||
return arg_type;
|
||||
|
||||
proto->arg_type[i] = arg_type;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *reg,
|
||||
const struct btf_type *ref_t,
|
||||
@@ -12046,16 +12115,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
|
||||
|
||||
args = (const struct btf_param *)(meta->func_proto + 1);
|
||||
nargs = btf_type_vlen(meta->func_proto);
|
||||
if (nargs > MAX_BPF_FUNC_ARGS) {
|
||||
verbose(env, "Function %s has %d > %d args\n", func_name, nargs,
|
||||
MAX_BPF_FUNC_ARGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (nargs > MAX_BPF_FUNC_REG_ARGS && !bpf_jit_supports_stack_args()) {
|
||||
verbose(env, "JIT does not support kfunc %s() with %d args\n",
|
||||
func_name, nargs);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
ret = check_outgoing_stack_args(env, caller, nargs);
|
||||
if (ret)
|
||||
@@ -12072,7 +12131,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
|
||||
int regno = reg_from_argno(argno);
|
||||
bool btf_id_fixed_off_ok = true;
|
||||
u32 ref_id, type_size;
|
||||
int kf_arg_type;
|
||||
int kf_arg_type = meta->fn->arg_type[i];
|
||||
|
||||
if (is_kfunc_arg_prog_aux(btf, &args[i])) {
|
||||
/* Reject repeated use bpf_prog_aux */
|
||||
@@ -12117,9 +12176,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
|
||||
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
|
||||
}
|
||||
|
||||
kf_arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
|
||||
if (kf_arg_type < 0)
|
||||
return kf_arg_type;
|
||||
|
||||
if (bpf_register_is_null(reg) && type_may_be_null(kf_arg_type))
|
||||
continue;
|
||||
@@ -12986,6 +13042,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
int err, insn_idx = *insn_idx_p;
|
||||
const struct btf_param *args;
|
||||
u32 i, nargs, ptr_type_id;
|
||||
struct bpf_kfunc_desc *desc;
|
||||
struct btf *desc_btf;
|
||||
int id;
|
||||
|
||||
@@ -13002,6 +13059,13 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
func_name = meta.func_name;
|
||||
insn_aux = &env->insn_aux_data[insn_idx];
|
||||
|
||||
desc = find_kfunc_desc(env->prog, insn->imm, insn->off);
|
||||
if (!desc) {
|
||||
verifier_bug(env, "kfunc descriptor not found for func_id %u", insn->imm);
|
||||
return -EFAULT;
|
||||
}
|
||||
meta.fn = &desc->proto;
|
||||
|
||||
insn_aux->is_iter_next = bpf_is_iter_next_kfunc(&meta);
|
||||
|
||||
if (!insn->off &&
|
||||
|
||||
Reference in New Issue
Block a user