summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAmery Hung <ameryhung@gmail.com>2026-08-01 00:46:33 -0700
committerKumar Kartikeya Dwivedi <memxor@gmail.com>2026-08-03 00:31:26 +0200
commita49b70400b9de06234eb99f87cf60217ed98cc0c (patch)
tree793ca0c879f0df3dcaf40136dfab4277400f220b /kernel
parent1690dcf27c7368d275cdcf73793aafe4d81007c9 (diff)
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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bpf/syscall.c4
-rw-r--r--kernel/bpf/verifier.c98
2 files changed, 85 insertions, 17 deletions
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index d6be7f49433c..8d111da88655 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.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;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 23a6355a38d3..b274004fccfd 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -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 &&