summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAmery Hung <ameryhung@gmail.com>2026-08-01 00:46:29 -0700
committerKumar Kartikeya Dwivedi <memxor@gmail.com>2026-08-03 00:29:16 +0200
commit90990ee10be8952d55063990088b9ad4af344e34 (patch)
treece044fb494497096eb435621b844d752ebb5ee1f /kernel
parent76fb08750481049796357f7636777bdf3c2be0a8 (diff)
bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE
A kfunc memory-pointer argument comes in two flavors: a fixed-size buffer whose access size is derived from the pointed-to BTF type, and a variable-size buffer paired with a following __sz/__szk size argument. Both were represented by separate kfunc_ptr_arg_type values (KF_ARG_PTR_TO_MEM vs KF_ARG_PTR_TO_MEM_SIZE) with the pointer classified as the latter when a size argument followed. Mirror how helpers describe the same distinction: classify both as KF_ARG_PTR_TO_MEM and OR in MEM_FIXED_SIZE for the fixed-size case, just as helpers use ARG_PTR_TO_MEM | MEM_FIXED_SIZE. The switches now key on base_type(kf_arg_type) so the flag rides along, and the KF_ARG_PTR_TO_MEM handler either resolves the size from BTF (MEM_FIXED_SIZE) or falls through to the mem/size-pair check, which validates the buffer against the following size register and skips it. No functional change. Currently, KF_ARG_MEM_SIZE and KF_ARG_CONST_MEM_SIZE are only reachable from ARG_PTR_TO_MEM fallthrough. A patch later will merge scalar checking into the same switch and remove the fallthrough. Signed-off-by: Amery Hung <ameryhung@gmail.com> Reviewed-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/bpf/20260801074633.1595644-15-ameryhung@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bpf/verifier.c38
1 files changed, 21 insertions, 17 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index bf03ba2fc04f..1ccf3b764c51 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11059,6 +11059,8 @@ static bool __btf_type_is_scalar_struct(struct bpf_verifier_env *env,
}
enum kfunc_ptr_arg_type {
+ KF_ARG_CONST_MEM_SIZE,
+ KF_ARG_MEM_SIZE,
KF_ARG_PTR_TO_CTX,
KF_ARG_PTR_TO_ALLOC_BTF_ID, /* Allocated object */
KF_ARG_PTR_TO_REFCOUNTED_KPTR, /* Refcounted local kptr */
@@ -11068,7 +11070,6 @@ enum kfunc_ptr_arg_type {
KF_ARG_PTR_TO_LIST_NODE,
KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */
KF_ARG_PTR_TO_MEM,
- KF_ARG_PTR_TO_MEM_SIZE, /* Size derived from next argument, skip it */
KF_ARG_PTR_TO_CALLBACK,
KF_ARG_PTR_TO_RB_ROOT,
KF_ARG_PTR_TO_RB_NODE,
@@ -11334,7 +11335,7 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
return meta->func_id == special_kfunc_list[KF_bpf_xdp_pull_data];
}
-static enum kfunc_ptr_arg_type
+static int
get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
struct bpf_reg_state *regs, struct bpf_call_arg_meta *meta,
const struct btf_type *t, const struct btf_type *ref_t,
@@ -11434,7 +11435,7 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
btf_type_str(ref_t), ref_tname, arg_mem_size ? "void, " : "");
return -EINVAL;
}
- return arg_mem_size ? KF_ARG_PTR_TO_MEM_SIZE : KF_ARG_PTR_TO_MEM;
+ return arg_mem_size ? KF_ARG_PTR_TO_MEM : KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
}
static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
@@ -12136,7 +12137,7 @@ 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);
}
- switch (kf_arg_type) {
+ switch (base_type(kf_arg_type)) {
case KF_ARG_PTR_TO_ALLOC_BTF_ID:
case KF_ARG_PTR_TO_BTF_ID:
if (!is_trusted_reg(env, reg)) {
@@ -12159,7 +12160,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
case KF_ARG_PTR_TO_RB_ROOT:
case KF_ARG_PTR_TO_RB_NODE:
case KF_ARG_PTR_TO_MEM:
- case KF_ARG_PTR_TO_MEM_SIZE:
case KF_ARG_PTR_TO_CALLBACK:
case KF_ARG_PTR_TO_CONST_STR:
case KF_ARG_PTR_TO_WORKQUEUE:
@@ -12190,7 +12190,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
- switch (kf_arg_type) {
+ switch (base_type(kf_arg_type)) {
case KF_ARG_PTR_TO_CTX:
if (reg->type != PTR_TO_CTX) {
verbose(env, "%s expected pointer to ctx, but got %s\n",
@@ -12387,18 +12387,22 @@ check_ok:
return ret;
break;
case KF_ARG_PTR_TO_MEM:
- resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
- if (IS_ERR(resolve_ret)) {
- verbose(env, "%s reference type('%s %s') size cannot be determined: %ld\n",
- reg_arg_name(env, argno), btf_type_str(ref_t),
- ref_tname, PTR_ERR(resolve_ret));
- return -EINVAL;
+ if (kf_arg_type & MEM_FIXED_SIZE) {
+ resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
+ if (IS_ERR(resolve_ret)) {
+ verbose(env, "%s reference type('%s %s') size cannot be determined: %ld\n",
+ reg_arg_name(env, argno), btf_type_str(ref_t),
+ ref_tname, PTR_ERR(resolve_ret));
+ return -EINVAL;
+ }
+ ret = check_mem_reg(env, reg, argno, type_size, BPF_READ | BPF_WRITE, meta);
+ if (ret < 0)
+ return ret;
+ break;
}
- ret = check_mem_reg(env, reg, argno, type_size, BPF_READ | BPF_WRITE, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_MEM_SIZE:
+ fallthrough;
+ case KF_ARG_CONST_MEM_SIZE:
+ case KF_ARG_MEM_SIZE:
{
struct bpf_reg_state *buff_reg = reg;
struct bpf_reg_state *size_reg = get_func_arg_reg(caller, regs, i + 1);