From: Amery Hung <ameryhung@gmail.com>
To: bpf@vger.kernel.org
Cc: alexei.starovoitov@gmail.com, andrii@kernel.org,
daniel@iogearbox.net, eddyz87@gmail.com, memxor@gmail.com,
ameryhung@gmail.com, kernel-team@meta.com
Subject: [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF
Date: Wed, 22 Jul 2026 22:08:05 -0700 [thread overview]
Message-ID: <20260723050806.1158442-18-ameryhung@gmail.com> (raw)
In-Reply-To: <20260723050806.1158442-1-ameryhung@gmail.com>
Pointer kfunc arguments are classified into a kfunc_ptr_arg_type by
get_kfunc_ptr_arg_type() from the BTF alone, then validated against the
register state in check_kfunc_args(). Scalar arguments, in contrast,
were special-cased in a block before the switch: the SCALAR_VALUE
check, the __k constant / __sz mem-size handling, and the rdonly/rdwr
return-buffer sizing were all done inline.
Give scalars the same treatment. Add kfunc_ptr_arg_type values for the
scalar kinds -- KF_ARG_CONST_BTF_ID (__k), KF_ARG_CONST_MEM_SIZE and
KF_ARG_MEM_SIZE (__sz), KF_ARG_CONST_ALLOC_SIZE_OR_ZERO (rdonly/rdwr
buffer size), and KF_ARG_ANYTHING -- and classify them from the BTF
suffix/name in get_kfunc_arg_type() (renamed from
get_kfunc_ptr_arg_type() now that it handles all argument kinds).
The register-state validation for each scalar kind moves into its own
case in the check_kfunc_args() switch, alongside the pointer cases. The
pointer-only preparation (NULL check and ref_t/ref_tname resolution) is
now guarded by btf_type_is_ptr().
This keeps BTF-based classification separate from register validation
for every argument, paving the way for generating the kfunc argument
prototype at add-call time. No functional change intended.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 124 ++++++++++++++++++++++++++++--------------
1 file changed, 82 insertions(+), 42 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 24858a38c34c..ba2608592bd5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11105,6 +11105,11 @@ enum kfunc_ptr_arg_type {
KF_ARG_PTR_TO_IRQ_FLAG,
KF_ARG_PTR_TO_RES_SPIN_LOCK,
KF_ARG_PTR_TO_TASK_WORK,
+ KF_ARG_CONST_BTF_ID, /* const scalar carrying a BTF type id */
+ KF_ARG_CONST_MEM_SIZE, /* const scalar sizing a preceding memory arg */
+ KF_ARG_CONST_ALLOC_SIZE_OR_ZERO, /* const scalar sizing a returned buffer */
+ KF_ARG_MEM_SIZE, /* scalar sizing a preceding memory arg */
+ KF_ARG_ANYTHING, /* unconstrained scalar */
};
enum special_kfunc_type {
@@ -11361,14 +11366,34 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
}
static int
-get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- const struct btf_type *t, const struct btf_type *ref_t,
- const char *ref_tname, const struct btf_param *args,
- int arg, int nargs, argno_t argno)
+get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
+ const struct btf_type *t, const struct btf_type *ref_t,
+ const char *ref_tname, const struct btf_param *args,
+ int arg, int nargs, argno_t argno)
{
bool arg_mem_size = false;
int arg_type;
+ /* Scalar arguments are classified from their BTF suffix/name alone. */
+ if (btf_type_is_scalar(t)) {
+ if (is_kfunc_arg_constant(meta->btf, &args[arg]))
+ return KF_ARG_CONST_BTF_ID;
+ if (is_kfunc_arg_const_mem_size(meta->btf, &args[arg]))
+ return KF_ARG_CONST_MEM_SIZE;
+ if (is_kfunc_arg_mem_size(meta->btf, &args[arg]))
+ return KF_ARG_MEM_SIZE;
+ if (is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdonly_buf_size") ||
+ is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdwr_buf_size"))
+ return KF_ARG_CONST_ALLOC_SIZE_OR_ZERO;
+ return KF_ARG_ANYTHING;
+ }
+
+ if (!btf_type_is_ptr(t)) {
+ verbose(env, "Unrecognized %s type %s\n",
+ reg_arg_name(env, argno), btf_type_str(t));
+ return -EINVAL;
+ }
+
/* In this function, we verify the kfunc's BTF as per the argument type,
* leaving the rest of the verification with respect to the register
* type to our caller. When a set of conditions hold in the BTF type of
@@ -12057,7 +12082,6 @@ 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;
- bool is_ret_buf_sz = false;
int kf_arg_type;
if (is_kfunc_arg_prog_aux(btf, &args[i])) {
@@ -12081,39 +12105,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
t = btf_type_skip_modifiers(btf, args[i].type, NULL);
- if (btf_type_is_scalar(t)) {
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- return -EINVAL;
- }
-
- if (is_kfunc_arg_constant(meta->btf, &args[i]) ||
- is_kfunc_arg_const_mem_size(meta->btf, &args[i])) {
- ret = process_const_arg(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- } else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
- meta->r0_rdonly = true;
- is_ret_buf_sz = true;
- } else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) {
- is_ret_buf_sz = true;
- }
-
- if (is_ret_buf_sz) {
- ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
- if (ret < 0)
- return ret;
- }
- continue;
- }
-
- if (!btf_type_is_ptr(t)) {
- verbose(env, "Unrecognized %s type %s\n",
- reg_arg_name(env, argno), btf_type_str(t));
- return -EINVAL;
- }
-
- if ((bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
+ if (btf_type_is_ptr(t) && (bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
!is_kfunc_arg_nullable(meta->btf, &args[i])) {
verbose(env, "Possibly NULL pointer passed to trusted %s\n",
reg_arg_name(env, argno));
@@ -12130,11 +12122,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (reg_is_referenced(env, reg))
update_ref_obj(&meta->ref_obj, reg);
- ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ if (btf_type_is_ptr(t)) {
+ ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
+ ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ }
- kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname,
- args, i, nargs, argno);
+ kf_arg_type = get_kfunc_arg_type(env, meta, t, ref_t, ref_tname,
+ args, i, nargs, argno);
if (kf_arg_type < 0)
return kf_arg_type;
@@ -12148,6 +12142,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
switch (base_type(kf_arg_type)) {
+ case KF_ARG_CONST_BTF_ID:
+ case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ case KF_ARG_ANYTHING:
case KF_ARG_PTR_TO_ALLOC_BTF_ID:
case KF_ARG_PTR_TO_BTF_ID:
case KF_ARG_CONST_MAP_PTR:
@@ -12157,6 +12154,8 @@ 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_CONST_MEM_SIZE:
+ case KF_ARG_MEM_SIZE:
case KF_ARG_PTR_TO_CALLBACK:
case KF_ARG_PTR_TO_CONST_STR:
case KF_ARG_PTR_TO_WORKQUEUE:
@@ -12188,6 +12187,34 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
switch (base_type(kf_arg_type)) {
+ case KF_ARG_CONST_BTF_ID:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ ret = process_const_arg(env, reg, argno, meta);
+ if (ret < 0)
+ return ret;
+ break;
+ case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size"))
+ meta->r0_rdonly = true;
+ ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
+ if (ret < 0)
+ return ret;
+ break;
+ case KF_ARG_ANYTHING:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ break;
case KF_ARG_PTR_TO_CTX:
if (reg->type != PTR_TO_CTX) {
verbose(env, "%s expected pointer to ctx, but got %s\n",
@@ -12435,6 +12462,19 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
}
break;
+ case KF_ARG_CONST_MEM_SIZE:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ ret = process_const_arg(env, reg, argno, meta);
+ if (ret < 0)
+ return ret;
+
+ fallthrough;
+ case KF_ARG_MEM_SIZE:
+ break;
case KF_ARG_PTR_TO_CALLBACK:
if (reg->type != PTR_TO_FUNC) {
verbose(env, "%s expected pointer to func\n", reg_arg_name(env, argno));
--
2.52.0
next prev parent reply other threads:[~2026-07-23 5:08 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 01/18] bpf: Drop process_timer_func wrappers Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs Amery Hung
2026-07-23 5:25 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 03/18] bpf: Split kfunc map argument into __const_map and __map Amery Hung
2026-07-23 5:35 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 04/18] bpf: Pass kfunc meta to mem and mem_size check Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 05/18] bpf: Check helper and kfunc mem+size arguments identically Amery Hung
2026-07-23 5:52 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments Amery Hung
2026-07-23 5:42 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way Amery Hung
2026-07-23 5:57 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 08/18] bpf: Express ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE | SCALAR_MAYBE_ZERO Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 09/18] bpf: Rename ARG_CONST_SIZE{,_OR_ZERO} to ARG_MEM_SIZE{,_OR_ZERO} Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 10/18] bpf: Fold __szk const size handling into the scalar arg path Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 11/18] bpf: Classify kfunc mem_size args from BTF without register state Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 12/18] bpf: Handle NULL kfunc pointer args without a KF_ARG_PTR_TO_NULL type Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 13/18] bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register Amery Hung
2026-07-23 7:27 ` sashiko-bot
2026-07-23 5:08 ` [PATCH bpf-next v1 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL Amery Hung
2026-07-23 5:08 ` Amery Hung [this message]
2026-07-23 8:01 ` [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF sashiko-bot
2026-07-23 5:08 ` [PATCH bpf-next v1 18/18] bpf: Generate kfunc argument prototype at add-call time Amery Hung
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260723050806.1158442-18-ameryhung@gmail.com \
--to=ameryhung@gmail.com \
--cc=alexei.starovoitov@gmail.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=kernel-team@meta.com \
--cc=memxor@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.