* [PATCH bpf-next v2 01/18] bpf: Drop process_timer_func wrappers
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
@ 2026-07-24 19:07 ` Amery Hung
2026-07-24 19:07 ` [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs Amery Hung
` (16 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:07 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Drop process_timer_{helper,kfunc}() since bpf_call_arg_meta is now
shared by helper and kfunc. Call process_timer_func() directly.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 52be0a118cce..e8c76abbf76a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7224,18 +7224,6 @@ static int process_timer_func(struct bpf_verifier_env *env, struct bpf_reg_state
return check_map_field_pointer(env, reg, argno, BPF_TIMER, map);
}
-static int process_timer_helper(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
- struct bpf_call_arg_meta *meta)
-{
- return process_timer_func(env, reg, argno, &meta->map);
-}
-
-static int process_timer_kfunc(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
- struct bpf_call_arg_meta *meta)
-{
- return process_timer_func(env, reg, argno, &meta->map);
-}
-
static int process_kptr_func(struct bpf_verifier_env *env, int regno,
struct bpf_call_arg_meta *meta)
{
@@ -8466,7 +8454,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
}
break;
case ARG_PTR_TO_TIMER:
- err = process_timer_helper(env, reg, argno, meta);
+ err = process_timer_func(env, reg, argno, &meta->map);
if (err)
return err;
break;
@@ -12514,7 +12502,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
reg_arg_name(env, argno));
return -EINVAL;
}
- ret = process_timer_kfunc(env, reg, argno, meta);
+ ret = process_timer_func(env, reg, argno, &meta->map);
if (ret < 0)
return ret;
break;
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
2026-07-24 19:07 ` [PATCH bpf-next v2 01/18] bpf: Drop process_timer_func wrappers Amery Hung
@ 2026-07-24 19:07 ` Amery Hung
2026-07-24 19:28 ` sashiko-bot
2026-07-24 19:07 ` [PATCH bpf-next v2 03/18] bpf: Split kfunc map argument into __const_map and __map Amery Hung
` (15 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:07 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Both the helper ARG_CONST_MAP_PTR and the kfunc KF_ARG_PTR_TO_MAP
recorded the map pointer in meta->map and, when a map was already
bound by a preceding timer/workqueue/task_work argument, rejected a
mismatching map.
Factor the logic into a single process_map_ptr_arg() used by both
paths. The bound-object name (timer, workqueue, or bpf_task_work) is
derived from the bound map's btf_record, and the register numbers in
the message are computed from the map argument position instead of
being hard-coded.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 97 ++++++++++++++++++++-----------------------
1 file changed, 44 insertions(+), 53 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e8c76abbf76a..a3429610b426 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8274,6 +8274,44 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
static bool can_elide_value_nullness(const struct bpf_map *map);
+static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, struct bpf_call_arg_meta *meta)
+{
+ /* Use map_uid (which is unique id of inner map) to reject:
+ * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
+ * inner_map2 = bpf_map_lookup_elem(outer_map, key2)
+ * if (inner_map1 && inner_map2) {
+ * timer = bpf_map_lookup_elem(inner_map1);
+ * if (timer)
+ * // mismatch would have been allowed
+ * bpf_timer_init(timer, inner_map2);
+ * }
+ *
+ * Comparing map_ptr is enough to distinguish normal and outer maps.
+ */
+ if (meta->map.ptr &&
+ (meta->map.ptr != reg->map_ptr || meta->map.uid != reg->map_uid)) {
+ argno_t obj_argno = argno_from_reg(reg_from_argno(argno) - 1);
+ struct btf_record *rec = meta->map.ptr->record;
+ const char *obj_name = "workqueue";
+
+ if (rec->timer_off >= 0)
+ obj_name = "timer";
+ else if (rec->task_work_off >= 0)
+ obj_name = "bpf_task_work";
+
+ verbose(env, "%s pointer in %s map_uid=%d ",
+ obj_name, reg_arg_name(env, obj_argno), meta->map.uid);
+ verbose(env, "doesn't match map pointer in %s map_uid=%d\n",
+ reg_arg_name(env, argno), reg->map_uid);
+ return -EINVAL;
+ }
+
+ meta->map.ptr = reg->map_ptr;
+ meta->map.uid = reg->map_uid;
+ return 0;
+}
+
static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
struct bpf_call_arg_meta *meta,
const struct bpf_func_proto *fn,
@@ -8349,29 +8387,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
switch (base_type(arg_type)) {
case ARG_CONST_MAP_PTR:
/* bpf_map_xxx(map_ptr) call: remember that map_ptr */
- if (meta->map.ptr) {
- /* Use map_uid (which is unique id of inner map) to reject:
- * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
- * inner_map2 = bpf_map_lookup_elem(outer_map, key2)
- * if (inner_map1 && inner_map2) {
- * timer = bpf_map_lookup_elem(inner_map1);
- * if (timer)
- * // mismatch would have been allowed
- * bpf_timer_init(timer, inner_map2);
- * }
- *
- * Comparing map_ptr is enough to distinguish normal and outer maps.
- */
- if (meta->map.ptr != reg->map_ptr ||
- meta->map.uid != reg->map_uid) {
- verbose(env,
- "timer pointer in R1 map_uid=%d doesn't match map pointer in R2 map_uid=%d\n",
- meta->map.uid, reg->map_uid);
- return -EINVAL;
- }
- }
- meta->map.ptr = reg->map_ptr;
- meta->map.uid = reg->map_uid;
+ err = process_map_ptr_arg(env, reg, argno, meta);
+ if (err)
+ return err;
break;
case ARG_PTR_TO_MAP_KEY:
/* bpf_map_xxx(..., map_ptr, ..., key) call:
@@ -12122,36 +12140,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
reg_arg_name(env, argno));
return -EINVAL;
}
- if (meta->map.ptr && (reg->map_ptr->record->wq_off >= 0 ||
- reg->map_ptr->record->task_work_off >= 0)) {
- /* Use map_uid (which is unique id of inner map) to reject:
- * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
- * inner_map2 = bpf_map_lookup_elem(outer_map, key2)
- * if (inner_map1 && inner_map2) {
- * wq = bpf_map_lookup_elem(inner_map1);
- * if (wq)
- * // mismatch would have been allowed
- * bpf_wq_init(wq, inner_map2);
- * }
- *
- * Comparing map_ptr is enough to distinguish normal and outer maps.
- */
- if (meta->map.ptr != reg->map_ptr ||
- meta->map.uid != reg->map_uid) {
- if (reg->map_ptr->record->task_work_off >= 0) {
- verbose(env,
- "bpf_task_work pointer in R2 map_uid=%d doesn't match map pointer in R3 map_uid=%d\n",
- meta->map.uid, reg->map_uid);
- return -EINVAL;
- }
- verbose(env,
- "workqueue pointer in R1 map_uid=%d doesn't match map pointer in R2 map_uid=%d\n",
- meta->map.uid, reg->map_uid);
- return -EINVAL;
- }
- }
- meta->map.ptr = reg->map_ptr;
- meta->map.uid = reg->map_uid;
+ ret = process_map_ptr_arg(env, reg, argno, meta);
+ if (ret < 0)
+ return ret;
fallthrough;
case KF_ARG_PTR_TO_ALLOC_BTF_ID:
case KF_ARG_PTR_TO_BTF_ID:
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs
2026-07-24 19:07 ` [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs Amery Hung
@ 2026-07-24 19:28 ` sashiko-bot
2026-07-24 20:49 ` Amery Hung
0 siblings, 1 reply; 30+ messages in thread
From: sashiko-bot @ 2026-07-24 19:28 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Low] Multi-line comment does not follow the required BPF subsystem comment style.
Pre-existing issues:
- [Critical] Pre-existing verifier panic when an array map is passed to kfuncs like `bpf_wq_init`.
- [High] Pre-existing logic bug allows mismatching maps to bypass safety checks in kfuncs.
--
--- Patch [2]: [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs ---
commit 08bfe87a36be7126ec3db2d3ab4783e72b62e453
Author: Amery Hung <ameryhung@gmail.com>
bpf: Unify const map ptr argument checking for helpers and kfuncs
Both the helper ARG_CONST_MAP_PTR and the kfunc KF_ARG_PTR_TO_MAP
recorded the map pointer in meta->map and, when a map was already
bound by a preceding timer/workqueue/task_work argument, rejected a
mismatching map. This logic is factored into a single process_map_ptr_arg()
used by both paths.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index e8c76abbf76a2..a3429610b426e 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -8274,6 +8274,44 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
>
> static bool can_elide_value_nullness(const struct bpf_map *map);
>
> +static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
> + argno_t argno, struct bpf_call_arg_meta *meta)
> +{
> + /* Use map_uid (which is unique id of inner map) to reject:
[Severity: Low]
This isn't a bug, but does this multi-line comment follow the preferred BPF
subsystem formatting?
According to the subsystem guidelines, multi-line comments should have the
opening /* on its own line:
/*
* Use map_uid (which is unique id of inner map) to reject:
* ...
*/
[ ... ]
> @@ -12122,36 +12140,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> reg_arg_name(env, argno));
> return -EINVAL;
> }
> - if (meta->map.ptr && (reg->map_ptr->record->wq_off >= 0 ||
> - reg->map_ptr->record->task_work_off >= 0)) {
[Severity: Critical]
This is a pre-existing issue that was not introduced by this patch (in fact,
the patch appears to fix it!), but could the old code crash the verifier here
if a BPF program called bpf_wq_init(wq, map, 0) and passed an array map
without BTF for the map argument?
Since an array map without BTF has a NULL record, evaluating
reg->map_ptr->record->wq_off would result in an unconditional NULL pointer
dereference, leading to a panic. This could be triggered by any user with
privileges to load BPF programs.
[Severity: High]
This is also a pre-existing issue that was not introduced by this patch, but
did this original condition allow mismatching maps to bypass safety checks?
If a BPF program called bpf_wq_init(wq, map, 0) with a valid workqueue but
passed a mismatched map (like a map containing only a bpf_timer), the
mismatched map would lack both wq_off and task_work_off. This would make the
condition evaluate to false, bypassing the mismatch rejection and potentially
leading to type confusion or memory corruption during runtime callbacks.
> - /* Use map_uid (which is unique id of inner map) to reject:
> - * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=2
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs
2026-07-24 19:28 ` sashiko-bot
@ 2026-07-24 20:49 ` Amery Hung
2026-07-24 21:29 ` Kumar Kartikeya Dwivedi
0 siblings, 1 reply; 30+ messages in thread
From: Amery Hung @ 2026-07-24 20:49 UTC (permalink / raw)
To: sashiko-reviews; +Cc: bpf
On Fri, Jul 24, 2026 at 12:28 PM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
>
> New issues:
> - [Low] Multi-line comment does not follow the required BPF subsystem comment style.
>
> Pre-existing issues:
> - [Critical] Pre-existing verifier panic when an array map is passed to kfuncs like `bpf_wq_init`.
> - [High] Pre-existing logic bug allows mismatching maps to bypass safety checks in kfuncs.
> --
>
> --- Patch [2]: [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs ---
> commit 08bfe87a36be7126ec3db2d3ab4783e72b62e453
> Author: Amery Hung <ameryhung@gmail.com>
>
> bpf: Unify const map ptr argument checking for helpers and kfuncs
>
> Both the helper ARG_CONST_MAP_PTR and the kfunc KF_ARG_PTR_TO_MAP
> recorded the map pointer in meta->map and, when a map was already
> bound by a preceding timer/workqueue/task_work argument, rejected a
> mismatching map. This logic is factored into a single process_map_ptr_arg()
> used by both paths.
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index e8c76abbf76a2..a3429610b426e 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -8274,6 +8274,44 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
> >
> > static bool can_elide_value_nullness(const struct bpf_map *map);
> >
> > +static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
> > + argno_t argno, struct bpf_call_arg_meta *meta)
> > +{
> > + /* Use map_uid (which is unique id of inner map) to reject:
>
> [Severity: Low]
> This isn't a bug, but does this multi-line comment follow the preferred BPF
> subsystem formatting?
>
> According to the subsystem guidelines, multi-line comments should have the
> opening /* on its own line:
>
> /*
> * Use map_uid (which is unique id of inner map) to reject:
> * ...
> */
>
> [ ... ]
>
> > @@ -12122,36 +12140,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> > reg_arg_name(env, argno));
> > return -EINVAL;
> > }
> > - if (meta->map.ptr && (reg->map_ptr->record->wq_off >= 0 ||
> > - reg->map_ptr->record->task_work_off >= 0)) {
>
> [Severity: Critical]
> This is a pre-existing issue that was not introduced by this patch (in fact,
> the patch appears to fix it!), but could the old code crash the verifier here
> if a BPF program called bpf_wq_init(wq, map, 0) and passed an array map
> without BTF for the map argument?
>
> Since an array map without BTF has a NULL record, evaluating
> reg->map_ptr->record->wq_off would result in an unconditional NULL pointer
> dereference, leading to a panic. This could be triggered by any user with
> privileges to load BPF programs.
[...]
>
> [Severity: High]
> This is also a pre-existing issue that was not introduced by this patch, but
> did this original condition allow mismatching maps to bypass safety checks?
>
> If a BPF program called bpf_wq_init(wq, map, 0) with a valid workqueue but
> passed a mismatched map (like a map containing only a bpf_timer), the
> mismatched map would lack both wq_off and task_work_off. This would make the
> condition evaluate to false, bypassing the mismatch rejection and potentially
> leading to type confusion or memory corruption during runtime callbacks.
>
> > - /* Use map_uid (which is unique id of inner map) to reject:
> > - * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
Yes and that is why this patch use meta->map.ptr->record that is
already verified. I can also separate it into a standalone fix
targeting bpf if needed.
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=2
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs
2026-07-24 20:49 ` Amery Hung
@ 2026-07-24 21:29 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 30+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-07-24 21:29 UTC (permalink / raw)
To: Amery Hung, sashiko-reviews; +Cc: bpf
On Fri Jul 24, 2026 at 10:49 PM CEST, Amery Hung wrote:
> On Fri, Jul 24, 2026 at 12:28 PM <sashiko-bot@kernel.org> wrote:
>>
>> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
>>
>> New issues:
>> - [Low] Multi-line comment does not follow the required BPF subsystem comment style.
>>
>> Pre-existing issues:
>> - [Critical] Pre-existing verifier panic when an array map is passed to kfuncs like `bpf_wq_init`.
>> - [High] Pre-existing logic bug allows mismatching maps to bypass safety checks in kfuncs.
>> --
>>
>> --- Patch [2]: [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs ---
>> commit 08bfe87a36be7126ec3db2d3ab4783e72b62e453
>> Author: Amery Hung <ameryhung@gmail.com>
>>
>> bpf: Unify const map ptr argument checking for helpers and kfuncs
>>
>> Both the helper ARG_CONST_MAP_PTR and the kfunc KF_ARG_PTR_TO_MAP
>> recorded the map pointer in meta->map and, when a map was already
>> bound by a preceding timer/workqueue/task_work argument, rejected a
>> mismatching map. This logic is factored into a single process_map_ptr_arg()
>> used by both paths.
>>
>> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> > index e8c76abbf76a2..a3429610b426e 100644
>> > --- a/kernel/bpf/verifier.c
>> > +++ b/kernel/bpf/verifier.c
>> > @@ -8274,6 +8274,44 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
>> >
>> > static bool can_elide_value_nullness(const struct bpf_map *map);
>> >
>> > +static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
>> > + argno_t argno, struct bpf_call_arg_meta *meta)
>> > +{
>> > + /* Use map_uid (which is unique id of inner map) to reject:
>>
>> [Severity: Low]
>> This isn't a bug, but does this multi-line comment follow the preferred BPF
>> subsystem formatting?
>>
>> According to the subsystem guidelines, multi-line comments should have the
>> opening /* on its own line:
>>
>> /*
>> * Use map_uid (which is unique id of inner map) to reject:
>> * ...
>> */
>>
>> [ ... ]
>>
>> > @@ -12122,36 +12140,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
>> > reg_arg_name(env, argno));
>> > return -EINVAL;
>> > }
>> > - if (meta->map.ptr && (reg->map_ptr->record->wq_off >= 0 ||
>> > - reg->map_ptr->record->task_work_off >= 0)) {
>>
>> [Severity: Critical]
>> This is a pre-existing issue that was not introduced by this patch (in fact,
>> the patch appears to fix it!), but could the old code crash the verifier here
>> if a BPF program called bpf_wq_init(wq, map, 0) and passed an array map
>> without BTF for the map argument?
>>
>> Since an array map without BTF has a NULL record, evaluating
>> reg->map_ptr->record->wq_off would result in an unconditional NULL pointer
>> dereference, leading to a panic. This could be triggered by any user with
>> privileges to load BPF programs.
>
> [...]
>
>>
>> [Severity: High]
>> This is also a pre-existing issue that was not introduced by this patch, but
>> did this original condition allow mismatching maps to bypass safety checks?
>>
>> If a BPF program called bpf_wq_init(wq, map, 0) with a valid workqueue but
>> passed a mismatched map (like a map containing only a bpf_timer), the
>> mismatched map would lack both wq_off and task_work_off. This would make the
>> condition evaluate to false, bypassing the mismatch rejection and potentially
>> leading to type confusion or memory corruption during runtime callbacks.
>>
>> > - /* Use map_uid (which is unique id of inner map) to reject:
>> > - * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
>
> Yes and that is why this patch use meta->map.ptr->record that is
> already verified. I can also separate it into a standalone fix
> targeting bpf if needed.
>
I think keeping it in your set is fine. Since this is not reported I doubt
anyone hit this in practice, though I agree it's worth fixing.
>>
>> --
>> Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=2
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH bpf-next v2 03/18] bpf: Split kfunc map argument into __const_map and __map
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
2026-07-24 19:07 ` [PATCH bpf-next v2 01/18] bpf: Drop process_timer_func wrappers Amery Hung
2026-07-24 19:07 ` [PATCH bpf-next v2 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs Amery Hung
@ 2026-07-24 19:07 ` Amery Hung
2026-07-24 19:07 ` [PATCH bpf-next v2 04/18] bpf: Pass kfunc meta to mem and mem_size check Amery Hung
` (14 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:07 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Kfuncs used a single '__map' suffix (KF_ARG_PTR_TO_MAP) for two different
things: a verifier-known map matched by map_uid against a bound
timer/wq/task_work object (bpf_wq_init, bpf_task_work_schedule*), and an
opaque 'struct bpf_map *' used only at runtime (bpf_arena_*), which may be a
map fd or a PTR_TO_BTF_ID struct bpf_map (e.g. a bpf_map iterator's ctx->map).
That combined path only accepted the btf map form due to type confusion. The
'if (!reg->map_ptr)' check reads reg->map_ptr, which aliases reg->btf in the
bpf_reg_state union. A PTR_TO_BTF_ID register always has a non-NULL reg->btf,
so the guard silently passed and validation fell through to
process_kf_arg_ptr_to_btf_id(). It also recorded PTR_TO_BTF_ID info in
meta->map, which would be meaningless.
Split the annotation to avoid such type confusion and to align with
helper:
- '__const_map' -> KF_ARG_CONST_MAP_PTR: verifier-known map, handled by
process_map_ptr_arg() like helper ARG_CONST_MAP_PTR.
- '__map' -> KF_ARG_PTR_TO_BTF_ID: opaque struct bpf_map, validated by
process_kf_arg_ptr_to_btf_id(). A map fd still matches via
reg2btf_ids[CONST_PTR_TO_MAP], so bpf_arena_alloc_pages(&map) keeps
working.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
Documentation/bpf/kfuncs.rst | 30 +++++++++++++++++++++++-
kernel/bpf/helpers.c | 16 ++++++-------
kernel/bpf/verifier.c | 45 +++++++++++++++++++++---------------
3 files changed, 64 insertions(+), 27 deletions(-)
diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index c801a330aece..cbde86d082cc 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -250,6 +250,34 @@ Or::
...
}
+2.3.7 __const_map and __map Annotations
+---------------------------------------
+
+These annotations are used for ``struct bpf_map *`` arguments and distinguish a
+verifier-known map from an opaque one.
+
+``__const_map`` indicates a map must be known at the verification time, i.e. a
+concrete map fd the BPF program references directly.
+
+An example is given below::
+
+ __bpf_kfunc int bpf_wq_init(struct bpf_wq *wq, void *p__const_map,
+ unsigned int flags)
+ {
+ ...
+ }
+
+``__map`` indicates an opaque ``struct bpf_map *`` that may be resolved
+at run time. The argument may take either a map fd or a ``PTR_TO_BTF_ID``
+``struct bpf_map`` pointer.
+
+An example is given below::
+
+ __bpf_kfunc void *bpf_arena_alloc_pages(void *p__map, ...)
+ {
+ ...
+ }
+
.. _BPF_kfunc_nodef:
2.4 Using an existing kernel function
@@ -411,7 +439,7 @@ Example declaration:
.. code-block:: c
__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
- void *map__map, bpf_task_work_callback_t callback,
+ void *map__const_map, bpf_task_work_callback_t callback,
struct bpf_prog_aux *aux) { ... }
Example usage in BPF program:
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index c18f1e16edee..93d0d9aa3e15 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3404,10 +3404,10 @@ __bpf_kfunc void bpf_throw(u64 cookie)
WARN(1, "A call to BPF exception callback should never return\n");
}
-__bpf_kfunc int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags)
+__bpf_kfunc int bpf_wq_init(struct bpf_wq *wq, void *p__const_map, unsigned int flags)
{
struct bpf_async_kern *async = (struct bpf_async_kern *)wq;
- struct bpf_map *map = p__map;
+ struct bpf_map *map = p__const_map;
BUILD_BUG_ON(sizeof(struct bpf_async_kern) > sizeof(struct bpf_wq));
BUILD_BUG_ON(__alignof__(struct bpf_async_kern) != __alignof__(struct bpf_wq));
@@ -4642,17 +4642,17 @@ static int bpf_task_work_schedule(struct task_struct *task, struct bpf_task_work
* mode
* @task: Task struct for which callback should be scheduled
* @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
- * @map__map: bpf_map that embeds struct bpf_task_work in the values
+ * @map__const_map: bpf_map that embeds struct bpf_task_work in the values
* @callback: pointer to BPF subprogram to call
* @aux: pointer to bpf_prog_aux of the caller BPF program, implicitly set by the verifier
*
* Return: 0 if task work has been scheduled successfully, negative error code otherwise
*/
__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
- void *map__map, bpf_task_work_callback_t callback,
+ void *map__const_map, bpf_task_work_callback_t callback,
struct bpf_prog_aux *aux)
{
- return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
+ return bpf_task_work_schedule(task, tw, map__const_map, callback, aux, TWA_SIGNAL);
}
/**
@@ -4660,17 +4660,17 @@ __bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct b
* mode
* @task: Task struct for which callback should be scheduled
* @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
- * @map__map: bpf_map that embeds struct bpf_task_work in the values
+ * @map__const_map: bpf_map that embeds struct bpf_task_work in the values
* @callback: pointer to BPF subprogram to call
* @aux: pointer to bpf_prog_aux of the caller BPF program, implicitly set by the verifier
*
* Return: 0 if task work has been scheduled successfully, negative error code otherwise
*/
__bpf_kfunc int bpf_task_work_schedule_resume(struct task_struct *task, struct bpf_task_work *tw,
- void *map__map, bpf_task_work_callback_t callback,
+ void *map__const_map, bpf_task_work_callback_t callback,
struct bpf_prog_aux *aux)
{
- return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_RESUME);
+ return bpf_task_work_schedule(task, tw, map__const_map, callback, aux, TWA_RESUME);
}
static int make_file_dynptr(struct file *file, u32 flags, bool may_sleep,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a3429610b426..bb0f3ea53c99 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10817,6 +10817,11 @@ static bool is_kfunc_arg_map(const struct btf *btf, const struct btf_param *arg)
return btf_param_match_suffix(btf, arg, "__map");
}
+static bool is_kfunc_arg_const_map(const struct btf *btf, const struct btf_param *arg)
+{
+ return btf_param_match_suffix(btf, arg, "__const_map");
+}
+
static bool is_kfunc_arg_alloc_obj(const struct btf *btf, const struct btf_param *arg)
{
return btf_param_match_suffix(btf, arg, "__alloc");
@@ -11063,7 +11068,7 @@ enum kfunc_ptr_arg_type {
KF_ARG_PTR_TO_RB_NODE,
KF_ARG_PTR_TO_NULL,
KF_ARG_PTR_TO_CONST_STR,
- KF_ARG_PTR_TO_MAP,
+ KF_ARG_CONST_MAP_PTR,
KF_ARG_PTR_TO_TIMER,
KF_ARG_PTR_TO_WORKQUEUE,
KF_ARG_PTR_TO_IRQ_FLAG,
@@ -11382,8 +11387,11 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_func_state *call
if (is_kfunc_arg_const_str(meta->btf, &args[arg]))
return KF_ARG_PTR_TO_CONST_STR;
+ if (is_kfunc_arg_const_map(meta->btf, &args[arg]))
+ return KF_ARG_CONST_MAP_PTR;
+
if (is_kfunc_arg_map(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_MAP;
+ return KF_ARG_PTR_TO_BTF_ID;
if (is_kfunc_arg_wq(meta->btf, &args[arg]))
return KF_ARG_PTR_TO_WORKQUEUE;
@@ -12131,19 +12139,15 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (kf_arg_type < 0)
return kf_arg_type;
+ if (is_kfunc_arg_map(btf, &args[i])) {
+ ref_id = *reg2btf_ids[CONST_PTR_TO_MAP];
+ ref_t = btf_type_by_id(btf_vmlinux, ref_id);
+ ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ }
+
switch (kf_arg_type) {
case KF_ARG_PTR_TO_NULL:
continue;
- case KF_ARG_PTR_TO_MAP:
- if (!reg->map_ptr) {
- verbose(env, "pointer in %s isn't map pointer\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- ret = process_map_ptr_arg(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- fallthrough;
case KF_ARG_PTR_TO_ALLOC_BTF_ID:
case KF_ARG_PTR_TO_BTF_ID:
if (!is_trusted_reg(env, reg)) {
@@ -12159,6 +12163,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
}
fallthrough;
+ case KF_ARG_CONST_MAP_PTR:
case KF_ARG_PTR_TO_ITER:
case KF_ARG_PTR_TO_LIST_HEAD:
case KF_ARG_PTR_TO_LIST_NODE:
@@ -12365,12 +12370,16 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_MAP:
- /* If argument has '__map' suffix expect 'struct bpf_map *' */
- ref_id = *reg2btf_ids[CONST_PTR_TO_MAP];
- ref_t = btf_type_by_id(btf_vmlinux, ref_id);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
- fallthrough;
+ case KF_ARG_CONST_MAP_PTR:
+ if (base_type(reg->type) != CONST_PTR_TO_MAP) {
+ verbose(env, "pointer in %s isn't map pointer\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ ret = process_map_ptr_arg(env, reg, argno, meta);
+ if (ret < 0)
+ return ret;
+ break;
case KF_ARG_PTR_TO_BTF_ID:
/* Only base_type is checked, further checks are done here */
if ((base_type(reg->type) != PTR_TO_BTF_ID ||
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 04/18] bpf: Pass kfunc meta to mem and mem_size check
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (2 preceding siblings ...)
2026-07-24 19:07 ` [PATCH bpf-next v2 03/18] bpf: Split kfunc map argument into __const_map and __map Amery Hung
@ 2026-07-24 19:07 ` Amery Hung
2026-07-24 19:07 ` [PATCH bpf-next v2 05/18] bpf: Check helper and kfunc mem+size arguments identically Amery Hung
` (13 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:07 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
kfunc now shares the same bpf_call_arg_meta with helpers. Pass kfunc's
own meta to check_mem_reg() and check_kfunc_mem_size() instead of NULL
or a temporary meta on the stack.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index bb0f3ea53c99..468702b09c50 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6924,7 +6924,7 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
}
static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
- argno_t argno, u32 mem_size)
+ argno_t argno, u32 mem_size, struct bpf_call_arg_meta *meta)
{
bool may_be_null = type_may_be_null(reg->type);
struct bpf_reg_state saved_reg;
@@ -6950,8 +6950,8 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
int size = base_type(reg->type) == PTR_TO_STACK ? -(int)mem_size : mem_size;
- err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, NULL);
- err = err ?: check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, NULL);
+ err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);
+ err = err ?: check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);
if (may_be_null)
*reg = saved_reg;
@@ -6994,22 +6994,20 @@ static int process_const_alloc_mem_size(struct bpf_verifier_env *env, struct bpf
}
static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *mem_reg,
- struct bpf_reg_state *size_reg, argno_t mem_argno, argno_t size_argno)
+ struct bpf_reg_state *size_reg, argno_t mem_argno,
+ argno_t size_argno, struct bpf_call_arg_meta *meta)
{
bool may_be_null = type_may_be_null(mem_reg->type);
struct bpf_reg_state saved_reg;
- struct bpf_call_arg_meta meta;
int err;
- memset(&meta, 0, sizeof(meta));
-
if (may_be_null) {
saved_reg = *mem_reg;
mark_ptr_not_null_reg(mem_reg);
}
- err = check_mem_size_reg(env, mem_reg, size_reg, mem_argno, size_argno, BPF_READ, true, &meta);
- err = err ?: check_mem_size_reg(env, mem_reg, size_reg, mem_argno, size_argno, BPF_WRITE, true, &meta);
+ err = check_mem_size_reg(env, mem_reg, size_reg, mem_argno, size_argno, BPF_READ, true, meta);
+ err = err ?: check_mem_size_reg(env, mem_reg, size_reg, mem_argno, size_argno, BPF_WRITE, true, meta);
if (may_be_null)
*mem_reg = saved_reg;
@@ -9258,7 +9256,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
ret = check_func_arg_reg_off(env, reg, argno, ARG_DONTCARE);
if (ret < 0)
return ret;
- if (check_mem_reg(env, reg, argno, arg->mem_size))
+ if (check_mem_reg(env, reg, argno, arg->mem_size, NULL))
return -EINVAL;
if (!(arg->arg_type & PTR_MAYBE_NULL) && (reg->type & PTR_MAYBE_NULL)) {
bpf_log(log, "%s is expected to be non-NULL\n",
@@ -12404,7 +12402,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
ref_tname, PTR_ERR(resolve_ret));
return -EINVAL;
}
- ret = check_mem_reg(env, reg, argno, type_size);
+ ret = check_mem_reg(env, reg, argno, type_size, meta);
if (ret < 0)
return ret;
break;
@@ -12418,7 +12416,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (!bpf_register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) {
ret = check_kfunc_mem_size_reg(env, buff_reg, size_reg,
- argno, next_argno);
+ argno, next_argno, meta);
if (ret < 0) {
verbose(env, "%s and ", reg_arg_name(env, argno));
verbose(env, "%s memory, len pair leads to invalid memory access\n",
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 05/18] bpf: Check helper and kfunc mem+size arguments identically
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (3 preceding siblings ...)
2026-07-24 19:07 ` [PATCH bpf-next v2 04/18] bpf: Pass kfunc meta to mem and mem_size check Amery Hung
@ 2026-07-24 19:07 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments Amery Hung
` (12 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:07 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Helper ARG_CONST_SIZE and kfunc KF_ARG_PTR_TO_MEM_SIZE memory
arguments already share check_mem_size_reg(), but only the kfunc wrapper
check_kfunc_mem_size_reg() ran mark_ptr_not_null_reg() on a possibly-NULL
pointer before the access check. That call does more than clearing
PTR_MAYBE_NULL: it converts special map-value-or-null registers to their
real non-NULL type, e.g. a map-in-map value to CONST_PTR_TO_MAP. Without
it the register keeps base type PTR_TO_MAP_VALUE and check_map_access()
treats it as raw data.
Because the helper path skipped that step, an un-narrowed map-of-maps
value could be passed to a nullable helper mem argument (e.g.
bpf_csum_diff()) and read as bytes, leaking the inner map descriptor (a
kernel pointer). The kfunc path already rejected the same program.
Handle the possibly-NULL pointer in the shared mem-argument checking so
helper and kfunc arguments are verified the same way, and drop the now
redundant check_kfunc_mem_size_reg() wrapper. Both paths now reject an
un-narrowed map-in-map value used as memory.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 49 +++++++++++++++++++------------------------
1 file changed, 22 insertions(+), 27 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 468702b09c50..2e56f726c12a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6873,7 +6873,14 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
bool zero_size_allowed,
struct bpf_call_arg_meta *meta)
{
- int err;
+ bool may_be_null = type_may_be_null(mem_reg->type);
+ struct bpf_reg_state saved_reg;
+ int err = 0;
+
+ if (may_be_null) {
+ saved_reg = *mem_reg;
+ mark_ptr_not_null_reg(mem_reg);
+ }
/* This is used to refine r0 return value bounds for helpers
* that enforce this value as an upper bound on return values.
@@ -6910,8 +6917,14 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
reg_arg_name(env, size_argno));
return -EACCES;
}
- err = check_helper_mem_access(env, mem_reg, mem_argno, reg_umax(size_reg),
- access_type, zero_size_allowed, meta);
+
+ if (access_type & BPF_READ)
+ err = check_helper_mem_access(env, mem_reg, mem_argno, reg_umax(size_reg),
+ BPF_READ, zero_size_allowed, meta);
+ if (!err && access_type & BPF_WRITE)
+ err = check_helper_mem_access(env, mem_reg, mem_argno, reg_umax(size_reg),
+ BPF_WRITE, zero_size_allowed, meta);
+
if (!err) {
int regno = reg_from_argno(size_argno);
@@ -6920,6 +6933,10 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
else
err = mark_stack_arg_precision(env, arg_idx_from_argno(size_argno));
}
+
+ if (may_be_null)
+ *mem_reg = saved_reg;
+
return err;
}
@@ -6993,28 +7010,6 @@ static int process_const_alloc_mem_size(struct bpf_verifier_env *env, struct bpf
return 0;
}
-static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *mem_reg,
- struct bpf_reg_state *size_reg, argno_t mem_argno,
- argno_t size_argno, struct bpf_call_arg_meta *meta)
-{
- bool may_be_null = type_may_be_null(mem_reg->type);
- struct bpf_reg_state saved_reg;
- int err;
-
- if (may_be_null) {
- saved_reg = *mem_reg;
- mark_ptr_not_null_reg(mem_reg);
- }
-
- err = check_mem_size_reg(env, mem_reg, size_reg, mem_argno, size_argno, BPF_READ, true, meta);
- err = err ?: check_mem_size_reg(env, mem_reg, size_reg, mem_argno, size_argno, BPF_WRITE, true, meta);
-
- if (may_be_null)
- *mem_reg = saved_reg;
-
- return err;
-}
-
enum {
PROCESS_SPIN_LOCK = (1 << 0),
PROCESS_RES_LOCK = (1 << 1),
@@ -12415,8 +12410,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
argno_t next_argno = argno_from_arg(i + 2);
if (!bpf_register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) {
- ret = check_kfunc_mem_size_reg(env, buff_reg, size_reg,
- argno, next_argno, meta);
+ ret = check_mem_size_reg(env, buff_reg, size_reg, argno, next_argno,
+ BPF_READ | BPF_WRITE, true, meta);
if (ret < 0) {
verbose(env, "%s and ", reg_arg_name(env, argno));
verbose(env, "%s memory, len pair leads to invalid memory access\n",
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (4 preceding siblings ...)
2026-07-24 19:07 ` [PATCH bpf-next v2 05/18] bpf: Check helper and kfunc mem+size arguments identically Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:25 ` sashiko-bot
2026-07-24 19:08 ` [PATCH bpf-next v2 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way Amery Hung
` (11 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Make sure the return of map-of-maps lookup cannot be passed to nullable
memory buffer argument for helper and kfunc.
- mapofmaps_value_as_kfunc_mem_buf: an un-narrowed (possibly-NULL)
map-of-maps value must not be usable as bpf_dynptr_slice()'s __nullable
buffer. mark_ptr_not_null_reg() converts such a value to
CONST_PTR_TO_MAP; without it check_map_access() would let the program
read the inner map descriptor as raw bytes.
- mapofmaps_value_as_helper_mem_buf: the same map-of-maps value passed to
a nullable helper mem argument (bpf_csum_diff()) must be rejected too,
guarding that helper and kfunc arguments are checked the same way.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
.../selftests/bpf/prog_tests/verifier.c | 2 +
.../bpf/progs/verifier_mem_size_reg.c | 60 +++++++++++++++++++
2 files changed, 62 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/verifier_mem_size_reg.c
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index be97f6887f0e..a81faa709dd5 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -67,6 +67,7 @@
#include "verifier_masking.skel.h"
#include "verifier_may_goto_1.skel.h"
#include "verifier_may_goto_2.skel.h"
+#include "verifier_mem_size_reg.skel.h"
#include "verifier_meta_access.skel.h"
#include "verifier_movsx.skel.h"
#include "verifier_mtu.skel.h"
@@ -221,6 +222,7 @@ void test_verifier_map_ret_val(void) { RUN(verifier_map_ret_val); }
void test_verifier_masking(void) { RUN(verifier_masking); }
void test_verifier_may_goto_1(void) { RUN(verifier_may_goto_1); }
void test_verifier_may_goto_2(void) { RUN(verifier_may_goto_2); }
+void test_verifier_mem_size_reg(void) { RUN(verifier_mem_size_reg); }
void test_verifier_meta_access(void) { RUN(verifier_meta_access); }
void test_verifier_movsx(void) { RUN(verifier_movsx); }
void test_verifier_mul(void) { RUN(verifier_mul); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_mem_size_reg.c b/tools/testing/selftests/bpf/progs/verifier_mem_size_reg.c
new file mode 100644
index 000000000000..c8eee1350027
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_mem_size_reg.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "bpf_kfuncs.h"
+
+char _license[] SEC("license") = "GPL";
+
+struct inner_map {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, int);
+} inner_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __array(values, struct inner_map);
+} outer_map SEC(".maps") = {
+ .values = { [0] = &inner_map },
+};
+
+/* An un-narrowed map-of-maps value must be rejected as a __nullable kfunc mem buffer */
+SEC("?tc")
+__failure __msg("type=map_ptr expected=fp")
+int mapofmaps_value_as_kfunc_mem_buf(struct __sk_buff *skb)
+{
+ struct bpf_dynptr dptr;
+ __u32 key = 0;
+ void *inner;
+ char *p;
+
+ inner = bpf_map_lookup_elem(&outer_map, &key);
+ /* intentionally NOT NULL-checked: reg stays PTR_TO_MAP_VALUE_OR_NULL */
+
+ bpf_dynptr_from_skb(skb, 0, &dptr);
+ /* pass the un-narrowed map-of-maps value as the scratch buffer */
+ p = bpf_dynptr_slice(&dptr, 0, inner, 4);
+ if (p)
+ return p[0];
+ return 0;
+}
+
+/* An un-narrowed map-of-maps value must be rejected as a PTR_MAYBE_NULL helper mem buffer */
+SEC("?tc")
+__failure __msg("type=map_ptr expected=fp")
+int mapofmaps_value_as_helper_mem_buf(struct __sk_buff *skb)
+{
+ __u32 key = 0;
+ void *inner;
+
+ inner = bpf_map_lookup_elem(&outer_map, &key);
+ /* intentionally NOT NULL-checked: reg stays PTR_TO_MAP_VALUE_OR_NULL */
+
+ /* @from is a nullable read-only mem+size arg; outer value_size is 4 */
+ return bpf_csum_diff(inner, 4, NULL, 0, 0) + skb->len;
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments
2026-07-24 19:08 ` [PATCH bpf-next v2 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments Amery Hung
@ 2026-07-24 19:25 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-24 19:25 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Incorrect argument name formatting in verifier log for map pointers passed on the stack in `process_map_ptr_arg()`.
--
--- Patch [6]: [PATCH bpf-next v2 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments ---
commit 26d0efc0fe7fbd940204152f62931d959ef53578
Author: Amery Hung <ameryhung@gmail.com>
selftests/bpf: Add tests for helper and kfunc mem+size arguments
This commit adds tests to ensure that the return of a map-of-maps lookup
cannot be passed to a nullable memory buffer argument for helpers and kfuncs.
It verifies that both mapofmaps_value_as_kfunc_mem_buf and
mapofmaps_value_as_helper_mem_buf are properly rejected by the verifier.
This guarantees that helper and kfunc arguments are checked in the same way.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 52be0a118cce0..2e56f726c12a8 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -8286,6 +8267,44 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
>
> static bool can_elide_value_nullness(const struct bpf_map *map);
>
> +static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
> + argno_t argno, struct bpf_call_arg_meta *meta)
> +{
[ ... ]
> + if (meta->map.ptr &&
> + (meta->map.ptr != reg->map_ptr || meta->map.uid != reg->map_uid)) {
> + argno_t obj_argno = argno_from_reg(reg_from_argno(argno) - 1);
[Severity: Low]
If the map argument is passed on the stack, does this incorrectly calculate
the preceding argument's argno? It appears that this can result in an invalid
internal representation for obj_argno.
> + struct btf_record *rec = meta->map.ptr->record;
> + const char *obj_name = "workqueue";
> +
> + if (rec->timer_off >= 0)
> + obj_name = "timer";
> + else if (rec->task_work_off >= 0)
> + obj_name = "bpf_task_work";
> +
> + verbose(env, "%s pointer in %s map_uid=%d ",
> + obj_name, reg_arg_name(env, obj_argno), meta->map.uid);
[Severity: Low]
Does this use the incorrectly computed obj_argno, causing reg_arg_name() to
print "R2" in the verifier log instead of the actual register or stack
location?
> + verbose(env, "doesn't match map pointer in %s map_uid=%d\n",
> + reg_arg_name(env, argno), reg->map_uid);
> + return -EINVAL;
> + }
> +
> + meta->map.ptr = reg->map_ptr;
> + meta->map.uid = reg->map_uid;
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=6
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH bpf-next v2 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (5 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:32 ` sashiko-bot
2026-07-24 19:08 ` [PATCH bpf-next v2 08/18] bpf: Express ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE | SCALAR_MAYBE_ZERO Amery Hung
` (10 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Fixed-size memory arguments went through two paths: helpers called
check_helper_mem_access() directly, while kfuncs and global subprogs
used check_mem_reg(). Route the helper MEM_FIXED_SIZE case through
check_mem_reg() too so all three share the same check.
This also fixes a bug in the helper path. When passing a NULL to
PTR_MAYBE_NULL | ARG_PTR_TO_FIXED_SIZE_MEM argument, the program would
be falsely rejected by check_helper_mem_access(). This is not
triggerable since there is no such kind of helper. Also, note that
check_reg_type() still make sure NULL cannot be passed to an argument
not marked with PTR_MAYBE_NULL.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2e56f726c12a..4606294e444a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6941,11 +6941,14 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
}
static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
- argno_t argno, u32 mem_size, struct bpf_call_arg_meta *meta)
+ argno_t argno, u32 mem_size, enum bpf_access_type access_type,
+ struct bpf_call_arg_meta *meta)
{
+ /* meta->btf is set only for kfuncs; a NULL meta means a global subprog. */
+ bool helper = meta && !meta->btf;
bool may_be_null = type_may_be_null(reg->type);
struct bpf_reg_state saved_reg;
- int err;
+ int size, err = 0;
if (bpf_register_is_null(reg))
return 0;
@@ -6965,10 +6968,12 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
mark_ptr_not_null_reg(reg);
}
- int size = base_type(reg->type) == PTR_TO_STACK ? -(int)mem_size : mem_size;
+ size = (!helper && base_type(reg->type) == PTR_TO_STACK) ? -(int)mem_size : mem_size;
- err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);
- err = err ?: check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);
+ if (access_type & BPF_READ)
+ err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);
+ if (!err && (access_type & BPF_WRITE))
+ err = check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);
if (may_be_null)
*reg = saved_reg;
@@ -8477,9 +8482,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
* next is_mem_size argument below.
*/
if (arg_type & MEM_FIXED_SIZE) {
- err = check_helper_mem_access(env, reg, argno, fn->arg_size[arg],
- arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
- false, meta);
+ err = check_mem_reg(env, reg, argno_from_reg(regno), fn->arg_size[arg],
+ arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta);
if (err)
return err;
if (arg_type & MEM_ALIGNED)
@@ -9251,7 +9255,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
ret = check_func_arg_reg_off(env, reg, argno, ARG_DONTCARE);
if (ret < 0)
return ret;
- if (check_mem_reg(env, reg, argno, arg->mem_size, NULL))
+ if (check_mem_reg(env, reg, argno, arg->mem_size, BPF_READ | BPF_WRITE, NULL))
return -EINVAL;
if (!(arg->arg_type & PTR_MAYBE_NULL) && (reg->type & PTR_MAYBE_NULL)) {
bpf_log(log, "%s is expected to be non-NULL\n",
@@ -12397,7 +12401,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
ref_tname, PTR_ERR(resolve_ret));
return -EINVAL;
}
- ret = check_mem_reg(env, reg, argno, type_size, meta);
+ ret = check_mem_reg(env, reg, argno, type_size, BPF_READ | BPF_WRITE, meta);
if (ret < 0)
return ret;
break;
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way
2026-07-24 19:08 ` [PATCH bpf-next v2 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way Amery Hung
@ 2026-07-24 19:32 ` sashiko-bot
2026-07-24 20:39 ` Amery Hung
0 siblings, 1 reply; 30+ messages in thread
From: sashiko-bot @ 2026-07-24 19:32 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] An integer overflow in `check_mem_reg` when calculating the stack access size allows global subprograms to bypass memory bounds checks, leading to out-of-bounds kernel memory accesses.
- [Critical] Global subprograms unconditionally accept literal NULL scalars for non-nullable pointer arguments, potentially leading to a kernel crash due to NULL pointer dereferences.
--
--- Patch [7]: [PATCH bpf-next v2 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way ---
commit 7f7a12884bb4c90a70c0a9e694af984eca478eee
Author: Amery Hung <ameryhung@gmail.com>
bpf: Check fixed-size mem args of helpers and kfuncs the same way
Fixed-size memory arguments went through two paths: helpers called
check_helper_mem_access() directly, while kfuncs and global subprogs
used check_mem_reg(). Route the helper MEM_FIXED_SIZE case through
check_mem_reg() too so all three share the same check.
This also fixes a bug in the helper path. When passing a NULL to
PTR_MAYBE_NULL | ARG_PTR_TO_FIXED_SIZE_MEM argument, the program would
be falsely rejected by check_helper_mem_access().
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 2e56f726c12a8..4606294e444a8 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -6965,10 +6968,12 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
> mark_ptr_not_null_reg(reg);
> }
>
> - int size = base_type(reg->type) == PTR_TO_STACK ? -(int)mem_size : mem_size;
> + size = (!helper && base_type(reg->type) == PTR_TO_STACK) ? -(int)mem_size : mem_size;
[Severity: Critical]
This is a pre-existing issue, but does the -(int)mem_size cast introduce an
integer overflow risk for global subprograms?
If a user defines a BTF struct with a size close to U32_MAX (e.g. 0xFFFFFFFC),
-(int)mem_size evaluates to a small positive integer like 4.
The verifier would only validate this small 4-byte region on the caller's
stack, while the global subprogram is verified separately with a PTR_TO_MEM
of the original massive size.
Could this allow the subprogram to read and write out-of-bounds kernel memory
beyond the provided stack object?
>
> - err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);
> - err = err ?: check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);
> + if (access_type & BPF_READ)
> + err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);
> + if (!err && (access_type & BPF_WRITE))
> + err = check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);
[ ... ]
> @@ -9251,7 +9255,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
> ret = check_func_arg_reg_off(env, reg, argno, ARG_DONTCARE);
> if (ret < 0)
> return ret;
> - if (check_mem_reg(env, reg, argno, arg->mem_size, NULL))
> + if (check_mem_reg(env, reg, argno, arg->mem_size, BPF_READ | BPF_WRITE, NULL))
> return -EINVAL;
> if (!(arg->arg_type & PTR_MAYBE_NULL) && (reg->type & PTR_MAYBE_NULL)) {
[Severity: Critical]
This isn't a bug introduced by this patch, but is it possible for global
subprograms to bypass nullability requirements here?
When check_mem_reg() is called just above this line, it returns 0 early for
literal NULL scalars:
kernel/bpf/verifier.c:check_mem_reg() {
if (bpf_register_is_null(reg))
return 0;
...
}
Because of this early return, the nullability check immediately following
check_mem_reg() in btf_check_func_arg_match() fails to catch the NULL.
The check relies on the PTR_MAYBE_NULL flag, which a literal NULL scalar
(SCALAR_VALUE) does not have.
Does this allow global subprograms to accept unvalidated NULL pointers
even for arguments not marked as nullable, leading to potential NULL
pointer dereferences?
> bpf_log(log, "%s is expected to be non-NULL\n",
> reg_arg_name(env, argno));
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=7
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way
2026-07-24 19:32 ` sashiko-bot
@ 2026-07-24 20:39 ` Amery Hung
0 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 20:39 UTC (permalink / raw)
To: sashiko-reviews; +Cc: bpf
[...]
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -6965,10 +6968,12 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
> > mark_ptr_not_null_reg(reg);
> > }
> >
> > - int size = base_type(reg->type) == PTR_TO_STACK ? -(int)mem_size : mem_size;
> > + size = (!helper && base_type(reg->type) == PTR_TO_STACK) ? -(int)mem_size : mem_size;
>
> [Severity: Critical]
> This is a pre-existing issue, but does the -(int)mem_size cast introduce an
> integer overflow risk for global subprograms?
>
> If a user defines a BTF struct with a size close to U32_MAX (e.g. 0xFFFFFFFC),
> -(int)mem_size evaluates to a small positive integer like 4.
This won't happen. The early "mem_size > S32_MAX" check in
check_mem_reg() will prevent this.
>
> The verifier would only validate this small 4-byte region on the caller's
> stack, while the global subprogram is verified separately with a PTR_TO_MEM
> of the original massive size.
>
> Could this allow the subprogram to read and write out-of-bounds kernel memory
> beyond the provided stack object?
>
> >
> > - err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);
> > - err = err ?: check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);
> > + if (access_type & BPF_READ)
> > + err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);
> > + if (!err && (access_type & BPF_WRITE))
> > + err = check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);
>
> [ ... ]
>
> > @@ -9251,7 +9255,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
> > ret = check_func_arg_reg_off(env, reg, argno, ARG_DONTCARE);
> > if (ret < 0)
> > return ret;
> > - if (check_mem_reg(env, reg, argno, arg->mem_size, NULL))
> > + if (check_mem_reg(env, reg, argno, arg->mem_size, BPF_READ | BPF_WRITE, NULL))
> > return -EINVAL;
> > if (!(arg->arg_type & PTR_MAYBE_NULL) && (reg->type & PTR_MAYBE_NULL)) {
>
> [Severity: Critical]
> This isn't a bug introduced by this patch, but is it possible for global
> subprograms to bypass nullability requirements here?
>
> When check_mem_reg() is called just above this line, it returns 0 early for
> literal NULL scalars:
>
> kernel/bpf/verifier.c:check_mem_reg() {
> if (bpf_register_is_null(reg))
> return 0;
> ...
> }
>
> Because of this early return, the nullability check immediately following
> check_mem_reg() in btf_check_func_arg_match() fails to catch the NULL.
> The check relies on the PTR_MAYBE_NULL flag, which a literal NULL scalar
> (SCALAR_VALUE) does not have.
This has been fixed in bpf.
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH bpf-next v2 08/18] bpf: Express ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE | SCALAR_MAYBE_ZERO
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (6 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 09/18] bpf: Rename ARG_CONST_SIZE{,_OR_ZERO} to ARG_MEM_SIZE{,_OR_ZERO} Amery Hung
` (9 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Add SCALAR_MAYBE_ZERO as an alias of PTR_MAYBE_NULL for scalar size
arguments, and define ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE |
SCALAR_MAYBE_ZERO instead of a distinct base type. This lets
check_func_arg() handle both with a single ARG_CONST_SIZE case, and the
redundant compatible_reg_types[] entry is dropped.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf.h | 4 +++-
kernel/bpf/verifier.c | 12 ++----------
2 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index e066f44a9c05..1d537ec557ec 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -748,6 +748,7 @@ extern const struct bpf_map_ops bpf_map_offload_ops;
enum bpf_type_flag {
/* PTR may be NULL. */
PTR_MAYBE_NULL = BIT(0 + BPF_BASE_TYPE_BITS),
+ SCALAR_MAYBE_ZERO = PTR_MAYBE_NULL,
/* MEM is read-only. When applied on bpf_arg, it indicates the arg is
* compatible with both mutable and immutable memory.
@@ -889,7 +890,6 @@ enum bpf_arg_type {
ARG_PTR_TO_ARENA,
ARG_CONST_SIZE, /* number of bytes accessed from memory */
- ARG_CONST_SIZE_OR_ZERO, /* number of bytes accessed from memory or 0 */
ARG_PTR_TO_CTX, /* pointer to context */
ARG_ANYTHING, /* any (initialized) argument is ok */
@@ -922,6 +922,8 @@ enum bpf_arg_type {
ARG_PTR_TO_UNINIT_MEM = MEM_UNINIT | MEM_WRITE | ARG_PTR_TO_MEM,
/* Pointer to valid memory of size known at compile time. */
ARG_PTR_TO_FIXED_SIZE_MEM = MEM_FIXED_SIZE | ARG_PTR_TO_MEM,
+ /* Number of bytes accessed from memory, or 0. */
+ ARG_CONST_SIZE_OR_ZERO = SCALAR_MAYBE_ZERO | ARG_CONST_SIZE,
/* This must be the last entry. Its purpose is to ensure the enum is
* wide enough to hold the higher bits reserved for bpf_type_flag.
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 4606294e444a..cd77aacad34f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7724,8 +7724,7 @@ static int process_iter_next_call(struct bpf_verifier_env *env, int insn_idx,
static bool arg_type_is_mem_size(enum bpf_arg_type type)
{
- return type == ARG_CONST_SIZE ||
- type == ARG_CONST_SIZE_OR_ZERO;
+ return base_type(type) == ARG_CONST_SIZE;
}
static bool arg_type_is_raw_mem(enum bpf_arg_type type)
@@ -7872,7 +7871,6 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
[ARG_PTR_TO_MAP_KEY] = &mem_types,
[ARG_PTR_TO_MAP_VALUE] = &mem_types,
[ARG_CONST_SIZE] = &scalar_types,
- [ARG_CONST_SIZE_OR_ZERO] = &scalar_types,
[ARG_CONST_ALLOC_SIZE_OR_ZERO] = &scalar_types,
[ARG_CONST_MAP_PTR] = &const_map_ptr_types,
[ARG_PTR_TO_CTX] = &context_types,
@@ -8494,13 +8492,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
argno_from_reg(regno - 1), argno,
fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
- false, meta);
- break;
- case ARG_CONST_SIZE_OR_ZERO:
- err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
- argno_from_reg(regno - 1), argno,
- fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
- true, meta);
+ arg_type & SCALAR_MAYBE_ZERO, meta);
break;
case ARG_PTR_TO_DYNPTR:
err = process_dynptr_func(env, reg, argno, insn_idx, arg_type, &meta->ref_obj,
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 09/18] bpf: Rename ARG_CONST_SIZE{,_OR_ZERO} to ARG_MEM_SIZE{,_OR_ZERO}
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (7 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 08/18] bpf: Express ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE | SCALAR_MAYBE_ZERO Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 10/18] bpf: Fold __szk const size handling into the scalar arg path Amery Hung
` (8 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
ARG_CONST_SIZE does not require a constant: check_mem_size_reg() accepts
any bounded scalar and verifies the memory access against its maximum
(reg_umax). Rename ARG_CONST_SIZE and ARG_CONST_SIZE_OR_ZERO to
ARG_MEM_SIZE and ARG_MEM_SIZE_OR_ZERO to reflect that. ARG_CONST_ALLOC_
SIZE_OR_ZERO, which does require a constant, is left unchanged.
Pure rename, no functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf.h | 4 +-
include/linux/bpf_verifier.h | 2 +-
kernel/bpf/backtrack.c | 2 +-
kernel/bpf/bpf_lsm.c | 4 +-
kernel/bpf/btf.c | 2 +-
kernel/bpf/cgroup.c | 8 +-
kernel/bpf/helpers.c | 26 ++--
kernel/bpf/ringbuf.c | 2 +-
kernel/bpf/stackmap.c | 10 +-
kernel/bpf/syscall.c | 4 +-
kernel/bpf/verifier.c | 6 +-
kernel/trace/bpf_trace.c | 62 +++++-----
net/core/filter.c | 116 +++++++++---------
.../bpf/progs/mem_rdonly_untrusted.c | 2 +-
.../selftests/bpf/progs/verifier_bounds.c | 2 +-
.../progs/verifier_helper_access_var_len.c | 6 +-
.../bpf/progs/verifier_helper_value_access.c | 2 +-
17 files changed, 130 insertions(+), 130 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 1d537ec557ec..2cbfa033a4eb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -889,7 +889,7 @@ enum bpf_arg_type {
ARG_PTR_TO_MEM, /* pointer to valid memory (stack, packet, map value) */
ARG_PTR_TO_ARENA,
- ARG_CONST_SIZE, /* number of bytes accessed from memory */
+ ARG_MEM_SIZE, /* number of bytes accessed from memory */
ARG_PTR_TO_CTX, /* pointer to context */
ARG_ANYTHING, /* any (initialized) argument is ok */
@@ -923,7 +923,7 @@ enum bpf_arg_type {
/* Pointer to valid memory of size known at compile time. */
ARG_PTR_TO_FIXED_SIZE_MEM = MEM_FIXED_SIZE | ARG_PTR_TO_MEM,
/* Number of bytes accessed from memory, or 0. */
- ARG_CONST_SIZE_OR_ZERO = SCALAR_MAYBE_ZERO | ARG_CONST_SIZE,
+ ARG_MEM_SIZE_OR_ZERO = SCALAR_MAYBE_ZERO | ARG_MEM_SIZE,
/* This must be the last entry. Its purpose is to ensure the enum is
* wide enough to hold the higher bits reserved for bpf_type_flag.
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 682c2cd3b844..bb0d43814e90 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -15,7 +15,7 @@
* ensures that umax_value + (int)off + (int)size cannot overflow a u64.
*/
#define BPF_MAX_VAR_OFF (1 << 29)
-/* Maximum variable size permitted for ARG_CONST_SIZE[_OR_ZERO]. This ensures
+/* Maximum variable size permitted for ARG_MEM_SIZE[_OR_ZERO]. This ensures
* that converting umax_value to int cannot overflow.
*/
#define BPF_MAX_VAR_SIZ (1 << 29)
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c
index 2e4ae0ef0860..2f473ad4fd7c 100644
--- a/kernel/bpf/backtrack.c
+++ b/kernel/bpf/backtrack.c
@@ -636,7 +636,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
* r5 += 1
* ...
* call bpf_perf_event_output#25
- * where .arg5_type = ARG_CONST_SIZE_OR_ZERO
+ * where .arg5_type = ARG_MEM_SIZE_OR_ZERO
*
* and this case:
* r6 = 1
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 3983b4ce73c8..82c5988417a0 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -186,7 +186,7 @@ static const struct bpf_func_proto bpf_ima_inode_hash_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &bpf_ima_inode_hash_btf_ids[0],
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.allowed = bpf_ima_inode_hash_allowed,
};
@@ -205,7 +205,7 @@ static const struct bpf_func_proto bpf_ima_file_hash_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &bpf_ima_file_hash_btf_ids[0],
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.allowed = bpf_ima_inode_hash_allowed,
};
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 4eeeaeb69790..5e8ac45ce56a 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8709,7 +8709,7 @@ const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_ANYTHING,
};
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 4355ccb78a9c..fb9357b64cad 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -2305,7 +2305,7 @@ static const struct bpf_func_proto bpf_sysctl_get_name_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -2347,7 +2347,7 @@ static const struct bpf_func_proto bpf_sysctl_get_current_value_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
BPF_CALL_3(bpf_sysctl_get_new_value, struct bpf_sysctl_kern *, ctx, char *, buf,
@@ -2367,7 +2367,7 @@ static const struct bpf_func_proto bpf_sysctl_get_new_value_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
BPF_CALL_3(bpf_sysctl_set_new_value, struct bpf_sysctl_kern *, ctx,
@@ -2393,7 +2393,7 @@ static const struct bpf_func_proto bpf_sysctl_set_new_value_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
static const struct bpf_func_proto *
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 93d0d9aa3e15..18be5d6e4f9c 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -278,7 +278,7 @@ const struct bpf_func_proto bpf_get_current_comm_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
};
#if defined(CONFIG_QUEUED_SPINLOCKS) || defined(CONFIG_BPF_ARCH_SPINLOCK)
@@ -539,7 +539,7 @@ const struct bpf_func_proto bpf_strtol_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
.arg4_size = sizeof(s64),
@@ -567,7 +567,7 @@ const struct bpf_func_proto bpf_strtoul_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
.arg4_size = sizeof(u64),
@@ -583,7 +583,7 @@ static const struct bpf_func_proto bpf_strncmp_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
.arg3_type = ARG_PTR_TO_CONST_STR,
};
@@ -627,7 +627,7 @@ const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto = {
.arg1_type = ARG_ANYTHING,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
};
static const struct bpf_func_proto bpf_get_raw_smp_processor_id_proto = {
@@ -653,7 +653,7 @@ const struct bpf_func_proto bpf_event_output_data_proto = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_3(bpf_copy_from_user, void *, dst, u32, size,
@@ -675,7 +675,7 @@ const struct bpf_func_proto bpf_copy_from_user_proto = {
.might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
@@ -706,7 +706,7 @@ const struct bpf_func_proto bpf_copy_from_user_task_proto = {
.might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_BTF_ID,
.arg4_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
@@ -1093,10 +1093,10 @@ const struct bpf_func_proto bpf_snprintf_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM_OR_NULL | MEM_WRITE,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_PTR_TO_CONST_STR,
.arg4_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
static void *map_key_from_value(struct bpf_map *map, void *value, u32 *arr_idx)
@@ -1888,7 +1888,7 @@ static const struct bpf_func_proto bpf_dynptr_from_mem_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT | MEM_WRITE,
};
@@ -1943,7 +1943,7 @@ static const struct bpf_func_proto bpf_dynptr_read_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_PTR_TO_DYNPTR,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
@@ -2004,7 +2004,7 @@ static const struct bpf_func_proto bpf_dynptr_write_proto = {
.arg1_type = ARG_PTR_TO_DYNPTR,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg4_type = ARG_MEM_SIZE_OR_ZERO,
.arg5_type = ARG_ANYTHING,
};
diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c
index 35ae64ade36b..c1bf7a197a96 100644
--- a/kernel/bpf/ringbuf.c
+++ b/kernel/bpf/ringbuf.c
@@ -634,7 +634,7 @@ const struct bpf_func_proto bpf_ringbuf_output_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index 41fe87d7302f..463f94ba1cc4 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -781,7 +781,7 @@ const struct bpf_func_proto bpf_get_stack_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -797,7 +797,7 @@ const struct bpf_func_proto bpf_get_stack_sleepable_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -831,7 +831,7 @@ const struct bpf_func_proto bpf_get_task_stack_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -848,7 +848,7 @@ const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -911,7 +911,7 @@ const struct bpf_func_proto bpf_get_stack_proto_pe = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 0ff9e3aa293d..67704ddd29cb 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6544,7 +6544,7 @@ static const struct bpf_func_proto bpf_sys_bpf_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_ANYTHING,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
const struct bpf_func_proto * __weak
@@ -6591,7 +6591,7 @@ static const struct bpf_func_proto bpf_kallsyms_lookup_name_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
.arg4_size = sizeof(u64),
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index cd77aacad34f..ce34a9575f15 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7724,7 +7724,7 @@ static int process_iter_next_call(struct bpf_verifier_env *env, int insn_idx,
static bool arg_type_is_mem_size(enum bpf_arg_type type)
{
- return base_type(type) == ARG_CONST_SIZE;
+ return base_type(type) == ARG_MEM_SIZE;
}
static bool arg_type_is_raw_mem(enum bpf_arg_type type)
@@ -7870,7 +7870,7 @@ static const struct bpf_reg_types dynptr_types = {
static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
[ARG_PTR_TO_MAP_KEY] = &mem_types,
[ARG_PTR_TO_MAP_VALUE] = &mem_types,
- [ARG_CONST_SIZE] = &scalar_types,
+ [ARG_MEM_SIZE] = &scalar_types,
[ARG_CONST_ALLOC_SIZE_OR_ZERO] = &scalar_types,
[ARG_CONST_MAP_PTR] = &const_map_ptr_types,
[ARG_PTR_TO_CTX] = &context_types,
@@ -8488,7 +8488,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
}
break;
- case ARG_CONST_SIZE:
+ case ARG_MEM_SIZE:
err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
argno_from_reg(regno - 1), argno,
fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 76ab51deaa6b..891897f8a1b3 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -221,7 +221,7 @@ const struct bpf_func_proto bpf_probe_read_user_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
@@ -258,7 +258,7 @@ const struct bpf_func_proto bpf_probe_read_user_str_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
@@ -273,7 +273,7 @@ const struct bpf_func_proto bpf_probe_read_kernel_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
@@ -308,7 +308,7 @@ const struct bpf_func_proto bpf_probe_read_kernel_str_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
@@ -328,7 +328,7 @@ static const struct bpf_func_proto bpf_probe_read_compat_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
@@ -347,7 +347,7 @@ static const struct bpf_func_proto bpf_probe_read_compat_str_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
#endif /* CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE */
@@ -383,7 +383,7 @@ static const struct bpf_func_proto bpf_probe_write_user_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_ANYTHING,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
#define MAX_TRACE_PRINTK_VARARGS 3
@@ -418,7 +418,7 @@ static const struct bpf_func_proto bpf_trace_printk_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
};
static void __set_printk_clr_event(struct work_struct *work)
@@ -474,9 +474,9 @@ static const struct bpf_func_proto bpf_trace_vprintk_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
.arg3_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg4_type = ARG_MEM_SIZE_OR_ZERO,
};
const struct bpf_func_proto *bpf_get_trace_vprintk_proto(void)
@@ -518,9 +518,9 @@ static const struct bpf_func_proto bpf_seq_printf_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_seq_file_ids[0],
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_3(bpf_seq_write, struct seq_file *, m, const void *, data, u32, len)
@@ -535,7 +535,7 @@ static const struct bpf_func_proto bpf_seq_write_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_seq_file_ids[0],
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_4(bpf_seq_printf_btf, struct seq_file *, m, struct btf_ptr *, ptr,
@@ -559,7 +559,7 @@ static const struct bpf_func_proto bpf_seq_printf_btf_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_seq_file_ids[0],
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -633,7 +633,7 @@ static const struct bpf_func_proto bpf_perf_event_read_value_proto = {
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
};
const struct bpf_func_proto *bpf_get_perf_event_read_value_proto(void)
@@ -730,7 +730,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
static DEFINE_PER_CPU(int, bpf_event_output_nest_level);
@@ -996,7 +996,7 @@ static const struct bpf_func_proto bpf_d_path_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &bpf_d_path_btf_ids[0],
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.allowed = bpf_d_path_allowed,
};
@@ -1053,9 +1053,9 @@ const struct bpf_func_proto bpf_snprintf_btf_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_WRITE,
- .arg2_type = ARG_CONST_SIZE,
+ .arg2_type = ARG_MEM_SIZE,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
.arg5_type = ARG_ANYTHING,
};
@@ -1218,7 +1218,7 @@ const struct bpf_func_proto bpf_get_branch_snapshot_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_3(get_func_arg, void *, ctx, u32, n, u64 *, value)
@@ -1421,7 +1421,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto_tp = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map,
@@ -1462,7 +1462,7 @@ static const struct bpf_func_proto bpf_get_stack_proto_tp = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -1524,12 +1524,12 @@ BPF_CALL_3(bpf_perf_prog_read_value, struct bpf_perf_event_data_kern *, ctx,
}
static const struct bpf_func_proto bpf_perf_prog_read_value_proto = {
- .func = bpf_perf_prog_read_value,
- .gpl_only = true,
- .ret_type = RET_INTEGER,
- .arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE,
+ .func = bpf_perf_prog_read_value,
+ .gpl_only = true,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_PTR_TO_UNINIT_MEM,
+ .arg3_type = ARG_MEM_SIZE,
};
BPF_CALL_4(bpf_read_branch_records, struct bpf_perf_event_data_kern *, ctx,
@@ -1566,7 +1566,7 @@ static const struct bpf_func_proto bpf_read_branch_records_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM_OR_NULL | MEM_WRITE,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -1646,7 +1646,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto_raw_tp = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
extern const struct bpf_func_proto bpf_skb_output_proto;
@@ -1701,7 +1701,7 @@ static const struct bpf_func_proto bpf_get_stack_proto_raw_tp = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
diff --git a/net/core/filter.c b/net/core/filter.c
index 0b7afdd0ae47..eb0b706af4b2 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1747,7 +1747,7 @@ static const struct bpf_func_proto bpf_skb_store_bytes_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
.arg5_type = ARG_ANYTHING,
};
@@ -1784,7 +1784,7 @@ static const struct bpf_func_proto bpf_skb_load_bytes_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
};
int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
@@ -1823,7 +1823,7 @@ static const struct bpf_func_proto bpf_flow_dissector_load_bytes_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_skb_load_bytes_relative, const struct sk_buff *, skb,
@@ -1867,7 +1867,7 @@ static const struct bpf_func_proto bpf_skb_load_bytes_relative_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
.arg5_type = ARG_ANYTHING,
};
@@ -2063,9 +2063,9 @@ static const struct bpf_func_proto bpf_csum_diff_proto = {
.pkt_access = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
- .arg2_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg4_type = ARG_MEM_SIZE_OR_ZERO,
.arg5_type = ARG_ANYTHING,
};
@@ -2621,7 +2621,7 @@ static const struct bpf_func_proto bpf_redirect_neigh_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_ANYTHING,
.arg2_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
};
@@ -4194,7 +4194,7 @@ static const struct bpf_func_proto bpf_xdp_load_bytes_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
};
int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
@@ -4226,7 +4226,7 @@ static const struct bpf_func_proto bpf_xdp_store_bytes_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
};
int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
@@ -4789,7 +4789,7 @@ static const struct bpf_func_proto bpf_skb_event_output_proto = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
BTF_ID_LIST_SINGLE(bpf_skb_output_btf_ids, struct, sk_buff)
@@ -4803,7 +4803,7 @@ const struct bpf_func_proto bpf_skb_output_proto = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
static unsigned short bpf_tunnel_key_af(u64 flags)
@@ -4886,7 +4886,7 @@ static const struct bpf_func_proto bpf_skb_get_tunnel_key_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -4921,7 +4921,7 @@ static const struct bpf_func_proto bpf_skb_get_tunnel_opt_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
static struct metadata_dst __percpu *md_dst;
@@ -5003,7 +5003,7 @@ static const struct bpf_func_proto bpf_skb_set_tunnel_key_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -5031,7 +5031,7 @@ static const struct bpf_func_proto bpf_skb_set_tunnel_opt_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
static const struct bpf_func_proto *
@@ -5203,7 +5203,7 @@ static const struct bpf_func_proto bpf_xdp_event_output_proto = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
BTF_ID_LIST_SINGLE(bpf_xdp_output_btf_ids, struct, xdp_buff)
@@ -5217,7 +5217,7 @@ const struct bpf_func_proto bpf_xdp_output_proto = {
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_1(bpf_get_socket_cookie, struct sk_buff *, skb)
@@ -5764,7 +5764,7 @@ const struct bpf_func_proto bpf_sk_setsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_sk_getsockopt, struct sock *, sk, int, level,
@@ -5781,7 +5781,7 @@ const struct bpf_func_proto bpf_sk_getsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_sk_setsockopt_nodelay, struct sock *, sk, int, level,
@@ -5805,7 +5805,7 @@ const struct bpf_func_proto bpf_sk_setsockopt_nodelay_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_unlocked_sk_setsockopt, struct sock *, sk, int, level,
@@ -5822,7 +5822,7 @@ const struct bpf_func_proto bpf_unlocked_sk_setsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_unlocked_sk_getsockopt, struct sock *, sk, int, level,
@@ -5839,7 +5839,7 @@ const struct bpf_func_proto bpf_unlocked_sk_getsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_sock_addr_setsockopt, struct bpf_sock_addr_kern *, ctx,
@@ -5856,7 +5856,7 @@ static const struct bpf_func_proto bpf_sock_addr_setsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_sock_addr_getsockopt, struct bpf_sock_addr_kern *, ctx,
@@ -5873,7 +5873,7 @@ static const struct bpf_func_proto bpf_sock_addr_getsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
static int sk_bpf_set_get_bypass_prot_mem(struct sock *sk,
@@ -5918,7 +5918,7 @@ static const struct bpf_func_proto bpf_sock_create_setsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_sock_create_getsockopt, struct sock *, sk, int, level,
@@ -5944,7 +5944,7 @@ static const struct bpf_func_proto bpf_sock_create_getsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_sock_ops_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
@@ -5970,7 +5970,7 @@ static const struct bpf_func_proto bpf_sock_ops_setsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
static int bpf_sock_ops_get_syn(struct bpf_sock_ops_kern *bpf_sock,
@@ -6080,7 +6080,7 @@ static const struct bpf_func_proto bpf_sock_ops_getsockopt_proto = {
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_2(bpf_sock_ops_cb_flags_set, struct bpf_sock_ops_kern *, bpf_sock,
@@ -6147,7 +6147,7 @@ static const struct bpf_func_proto bpf_bind_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
};
#ifdef CONFIG_XFRM
@@ -6200,7 +6200,7 @@ static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
.arg5_type = ARG_ANYTHING,
};
#endif
@@ -6607,7 +6607,7 @@ static const struct bpf_func_proto bpf_xdp_fib_lookup_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -6667,7 +6667,7 @@ static const struct bpf_func_proto bpf_skb_fib_lookup_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -6865,7 +6865,7 @@ static const struct bpf_func_proto bpf_lwt_in_push_encap_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE
+ .arg4_type = ARG_MEM_SIZE
};
static const struct bpf_func_proto bpf_lwt_xmit_push_encap_proto = {
@@ -6875,7 +6875,7 @@ static const struct bpf_func_proto bpf_lwt_xmit_push_encap_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE
+ .arg4_type = ARG_MEM_SIZE
};
#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
@@ -6919,7 +6919,7 @@ static const struct bpf_func_proto bpf_lwt_seg6_store_bytes_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE
+ .arg4_type = ARG_MEM_SIZE
};
static void bpf_update_srh_state(struct sk_buff *skb)
@@ -7008,7 +7008,7 @@ static const struct bpf_func_proto bpf_lwt_seg6_action_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg4_type = ARG_CONST_SIZE
+ .arg4_type = ARG_MEM_SIZE
};
BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset,
@@ -7249,7 +7249,7 @@ static const struct bpf_func_proto bpf_skc_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7268,7 +7268,7 @@ static const struct bpf_func_proto bpf_sk_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7287,7 +7287,7 @@ static const struct bpf_func_proto bpf_sk_lookup_udp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7311,7 +7311,7 @@ static const struct bpf_func_proto bpf_tc_skc_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7335,7 +7335,7 @@ static const struct bpf_func_proto bpf_tc_sk_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7359,7 +7359,7 @@ static const struct bpf_func_proto bpf_tc_sk_lookup_udp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7397,7 +7397,7 @@ static const struct bpf_func_proto bpf_xdp_sk_lookup_udp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7421,7 +7421,7 @@ static const struct bpf_func_proto bpf_xdp_skc_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7445,7 +7445,7 @@ static const struct bpf_func_proto bpf_xdp_sk_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7465,7 +7465,7 @@ static const struct bpf_func_proto bpf_sock_addr_skc_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7484,7 +7484,7 @@ static const struct bpf_func_proto bpf_sock_addr_sk_lookup_tcp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7503,7 +7503,7 @@ static const struct bpf_func_proto bpf_sock_addr_sk_lookup_udp_proto = {
.ret_type = RET_PTR_TO_SOCKET_OR_NULL,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
.arg4_type = ARG_ANYTHING,
.arg5_type = ARG_ANYTHING,
};
@@ -7823,9 +7823,9 @@ static const struct bpf_func_proto bpf_tcp_check_syncookie_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len,
@@ -7892,9 +7892,9 @@ static const struct bpf_func_proto bpf_tcp_gen_syncookie_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg5_type = ARG_CONST_SIZE,
+ .arg5_type = ARG_MEM_SIZE,
};
BPF_CALL_3(bpf_sk_assign, struct sk_buff *, skb, struct sock *, sk, u64, flags)
@@ -8048,7 +8048,7 @@ static const struct bpf_func_proto bpf_sock_ops_load_hdr_opt_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -8126,7 +8126,7 @@ static const struct bpf_func_proto bpf_sock_ops_store_hdr_opt_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE,
+ .arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -8221,7 +8221,7 @@ static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv4_proto = {
.arg1_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_RDONLY,
.arg1_size = sizeof(struct iphdr),
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_3(bpf_tcp_raw_gen_syncookie_ipv6, struct ipv6hdr *, iph,
@@ -8253,7 +8253,7 @@ static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv6_proto = {
.arg1_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_RDONLY,
.arg1_size = sizeof(struct ipv6hdr),
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
- .arg3_type = ARG_CONST_SIZE_OR_ZERO,
+ .arg3_type = ARG_MEM_SIZE_OR_ZERO,
};
BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv4, struct iphdr *, iph,
@@ -11726,7 +11726,7 @@ static const struct bpf_func_proto sk_reuseport_load_bytes_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
};
BPF_CALL_5(sk_reuseport_load_bytes_relative,
@@ -11744,7 +11744,7 @@ static const struct bpf_func_proto sk_reuseport_load_bytes_relative_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
- .arg4_type = ARG_CONST_SIZE,
+ .arg4_type = ARG_MEM_SIZE,
.arg5_type = ARG_ANYTHING,
};
diff --git a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c
index 5b4453747c23..0952d1ebf0f1 100644
--- a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c
+++ b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c
@@ -137,7 +137,7 @@ int helper_param_not_ok(void *ctx)
p = bpf_rdonly_cast(0, 0);
/*
- * Any helper with ARG_CONST_SIZE_OR_ZERO constraint will do,
+ * Any helper with ARG_MEM_SIZE_OR_ZERO constraint will do,
* the most permissive constraint
*/
bpf_copy_from_user(p, 0, (void *)42);
diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds.c b/tools/testing/selftests/bpf/progs/verifier_bounds.c
index bc038ac2df98..1a273e416fed 100644
--- a/tools/testing/selftests/bpf/progs/verifier_bounds.c
+++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c
@@ -1195,7 +1195,7 @@ l0_%=: r1 = r6; \
r3 += -8; \
r5 = 0; \
/* The 4th argument of bpf_skb_store_bytes is defined as \
- * ARG_CONST_SIZE, so 0 is not allowed. The 'r4 != 0' \
+ * ARG_MEM_SIZE, so 0 is not allowed. The 'r4 != 0' \
* is providing us this exclusion of zero from initial \
* [0, 7] range. \
*/ \
diff --git a/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c b/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c
index f2c54e4d89eb..343fc08d9747 100644
--- a/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c
+++ b/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c
@@ -85,7 +85,7 @@ __naked void stack_bitwise_and_zero_included(void)
r2 += -64; \
r4 = 0; \
/* Call bpf_ringbuf_output(), it is one of a few helper functions with\
- * ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.\
+ * ARG_MEM_SIZE_OR_ZERO parameter allowed in unpriv mode.\
* For unpriv this should signal an error, because memory at &fp[-64] is\
* not initialized. \
*/ \
@@ -278,7 +278,7 @@ __naked void stack_jmp_no_min_check(void)
r2 += -64; \
r4 = 0; \
/* Call bpf_ringbuf_output(), it is one of a few helper functions with\
- * ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.\
+ * ARG_MEM_SIZE_OR_ZERO parameter allowed in unpriv mode.\
* For unpriv this should signal an error, because memory at &fp[-64] is\
* not initialized. \
*/ \
@@ -778,7 +778,7 @@ __naked void variable_memory_8_bytes_leak(void)
r3 += 1; \
r4 = 0; \
/* Call bpf_ringbuf_output(), it is one of a few helper functions with\
- * ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.\
+ * ARG_MEM_SIZE_OR_ZERO parameter allowed in unpriv mode.\
* For unpriv this should signal an error, because memory region [1, 64]\
* at &fp[-64] is not fully initialized. \
*/ \
diff --git a/tools/testing/selftests/bpf/progs/verifier_helper_value_access.c b/tools/testing/selftests/bpf/progs/verifier_helper_value_access.c
index 6d2a38597c34..c6603a118fdc 100644
--- a/tools/testing/selftests/bpf/progs/verifier_helper_value_access.c
+++ b/tools/testing/selftests/bpf/progs/verifier_helper_value_access.c
@@ -91,7 +91,7 @@ l0_%=: exit; \
/* Call a function taking a pointer and a size which doesn't allow the size to
* be zero (i.e. bpf_trace_printk() declares the second argument to be
- * ARG_CONST_SIZE, not ARG_CONST_SIZE_OR_ZERO). We attempt to pass zero for the
+ * ARG_MEM_SIZE, not ARG_MEM_SIZE_OR_ZERO). We attempt to pass zero for the
* size and expect to fail.
*/
SEC("tracepoint")
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 10/18] bpf: Fold __szk const size handling into the scalar arg path
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (8 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 09/18] bpf: Rename ARG_CONST_SIZE{,_OR_ZERO} to ARG_MEM_SIZE{,_OR_ZERO} Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state Amery Hung
` (7 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
To align helper and kfunc pointer to memory argument handling, move
kfunc constant memorry size argument handling to the kfunc scalar
section. In addition, factor out constant scalar argument handling.
The constant size argument (__szk) of a kfunc memory/size pair was
recorded into meta->arg_constant by a dedicated block in the
KF_ARG_PTR_TO_MEM_SIZE case, duplicating the "only one constant
argument" and "must be a known constant" checks already in the generic
scalar argument handling. That block also did an explicit i++ to skip
the size argument.
This also fixes a precision gap: the old dedicated block did not mark
the size register precise, relying on check_mem_size_reg() for that. But
check_mem_size_reg() is skipped when the buffer is a nullable arg passed
as NULL (e.g. bpf_dynptr_slice(_rdwr) with a NULL buffer), so in that
case the __szk value was recorded and used for regs[R0].mem_size without
marking it precise. Routing the size through the scalar path marks it
precise in all cases.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf_verifier.h | 11 +++---
kernel/bpf/verifier.c | 66 +++++++++++++++++-------------------
2 files changed, 39 insertions(+), 38 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index bb0d43814e90..b54c1a5c9b11 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1479,6 +1479,12 @@ struct ret_mem_desc {
bool found;
};
+/* A constant scalar argument; Populated by process_const_arg() */
+struct arg_constant_desc {
+ u64 value;
+ bool found;
+};
+
struct bpf_call_arg_meta {
/* Common */
struct btf *btf;
@@ -1496,10 +1502,7 @@ struct bpf_call_arg_meta {
u32 kfunc_flags;
const struct btf_type *func_proto;
const char *func_name;
- struct {
- u64 value;
- bool found;
- } arg_constant;
+ struct arg_constant_desc arg_constant;
/* arg_{btf,btf_id,owning_ref} are used by kfunc-specific handling,
* generally to pass info about user-defined local kptr types to later
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index ce34a9575f15..0f8d40308317 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7015,6 +7015,35 @@ static int process_const_alloc_mem_size(struct bpf_verifier_env *env, struct bpf
return 0;
}
+static int process_const_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, struct bpf_call_arg_meta *meta)
+{
+ int regno = reg_from_argno(argno);
+ int err;
+
+ if (meta->arg_constant.found) {
+ verifier_bug(env, "only one constant argument permitted");
+ return -EFAULT;
+ }
+
+ if (!tnum_is_const(reg->var_off)) {
+ verbose(env, "%s must be a known constant\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ if (regno >= 0)
+ err = mark_chain_precision(env, regno);
+ else
+ err = mark_stack_arg_precision(env, arg_idx_from_argno(argno));
+ if (err < 0)
+ return err;
+
+ meta->arg_constant.found = true;
+ meta->arg_constant.value = reg->var_off.value;
+
+ return 0;
+}
+
enum {
PROCESS_SPIN_LOCK = (1 << 0),
PROCESS_RES_LOCK = (1 << 1),
@@ -12064,24 +12093,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return -EINVAL;
}
- if (is_kfunc_arg_constant(meta->btf, &args[i])) {
- if (meta->arg_constant.found) {
- verifier_bug(env, "only one constant argument permitted");
- return -EFAULT;
- }
- if (!tnum_is_const(reg->var_off)) {
- verbose(env, "%s must be a known constant\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (regno >= 0)
- ret = mark_chain_precision(env, regno);
- else
- ret = mark_stack_arg_precision(env, i);
+ if (is_kfunc_arg_constant(meta->btf, &args[i]) ||
+ is_kfunc_arg_const_mem_size(meta->btf, &args[i], reg)) {
+ ret = process_const_arg(env, reg, argno, meta);
if (ret < 0)
return ret;
- meta->arg_constant.found = true;
- meta->arg_constant.value = reg->var_off.value;
} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
meta->r0_rdonly = true;
is_ret_buf_sz = true;
@@ -12402,7 +12418,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
struct bpf_reg_state *buff_reg = reg;
const struct btf_param *buff_arg = &args[i];
struct bpf_reg_state *size_reg = get_func_arg_reg(caller, regs, i + 1);
- const struct btf_param *size_arg = &args[i + 1];
argno_t next_argno = argno_from_arg(i + 2);
if (!bpf_register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) {
@@ -12415,23 +12430,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
}
}
-
- if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
- if (meta->arg_constant.found) {
- verifier_bug(env, "only one constant argument permitted");
- return -EFAULT;
- }
- if (!tnum_is_const(size_reg->var_off)) {
- verbose(env, "%s must be a known constant\n",
- reg_arg_name(env, next_argno));
- return -EINVAL;
- }
- meta->arg_constant.found = true;
- meta->arg_constant.value = size_reg->var_off.value;
- }
-
- /* Skip next '__sz' or '__szk' argument */
- i++;
break;
}
case KF_ARG_PTR_TO_CALLBACK:
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (9 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 10/18] bpf: Fold __szk const size handling into the scalar arg path Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:38 ` sashiko-bot
2026-07-24 19:08 ` [PATCH bpf-next v2 12/18] bpf: Handle NULL kfunc pointer args without a KF_ARG_PTR_TO_NULL type Amery Hung
` (6 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_kfunc_args() already makes sure a scalar value is passed to a
scalar kfunc argument. Drop the check in is_kfunc_arg_mem_size() and
is_kfunc_arg_const_mem_size() to further decouple
get_kfunc_ptr_arg_type() from register state (a prerequisite for
generating a helper-like prototype from kfunc's BTF).
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0f8d40308317..f6b373630ca9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10795,26 +10795,24 @@ static bool is_kfunc_rcu_protected(struct bpf_call_arg_meta *meta)
}
static bool is_kfunc_arg_mem_size(const struct btf *btf,
- const struct btf_param *arg,
- const struct bpf_reg_state *reg)
+ const struct btf_param *arg)
{
const struct btf_type *t;
t = btf_type_skip_modifiers(btf, arg->type, NULL);
- if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
+ if (!btf_type_is_scalar(t))
return false;
return btf_param_match_suffix(btf, arg, "__sz");
}
static bool is_kfunc_arg_const_mem_size(const struct btf *btf,
- const struct btf_param *arg,
- const struct bpf_reg_state *reg)
+ const struct btf_param *arg)
{
const struct btf_type *t;
t = btf_type_skip_modifiers(btf, arg->type, NULL);
- if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
+ if (!btf_type_is_scalar(t))
return false;
return btf_param_match_suffix(btf, arg, "__szk");
@@ -11348,7 +11346,7 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
}
static enum kfunc_ptr_arg_type
-get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_func_state *caller,
+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,
const char *ref_tname, const struct btf_param *args,
@@ -11362,8 +11360,8 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_func_state *call
return KF_ARG_PTR_TO_CTX;
if (arg + 1 < nargs &&
- (is_kfunc_arg_mem_size(meta->btf, &args[arg + 1], get_func_arg_reg(caller, regs, arg + 1)) ||
- is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1], get_func_arg_reg(caller, regs, arg + 1))))
+ (is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
+ is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1])))
arg_mem_size = true;
/* In this function, we verify the kfunc's BTF as per the argument type,
@@ -12094,7 +12092,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
if (is_kfunc_arg_constant(meta->btf, &args[i]) ||
- is_kfunc_arg_const_mem_size(meta->btf, &args[i], reg)) {
+ is_kfunc_arg_const_mem_size(meta->btf, &args[i])) {
ret = process_const_arg(env, reg, argno, meta);
if (ret < 0)
return ret;
@@ -12139,7 +12137,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
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, caller, regs, meta, t, ref_t, ref_tname,
+ kf_arg_type = get_kfunc_ptr_arg_type(env, regs, meta, t, ref_t, ref_tname,
args, i, nargs, argno, reg);
if (kf_arg_type < 0)
return kf_arg_type;
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state
2026-07-24 19:08 ` [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state Amery Hung
@ 2026-07-24 19:38 ` sashiko-bot
2026-07-24 22:52 ` Amery Hung
0 siblings, 1 reply; 30+ messages in thread
From: sashiko-bot @ 2026-07-24 19:38 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Removing the `SCALAR_VALUE` check from `is_kfunc_arg_mem_size()` and `is_kfunc_arg_const_mem_size()` allows unvalidated pointer registers to be passed as memory size arguments during kfunc lookahead validation, triggering a `verifier_bug` kernel warning splat and local DoS.
--
--- Patch [11]: [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state ---
commit 55013229ec44003dda12328783652d410aa4f75e
Author: Amery Hung <ameryhung@gmail.com>
bpf: Classify kfunc mem_size args from BTF without register state
This commit drops the scalar register type check in is_kfunc_arg_mem_size()
and is_kfunc_arg_const_mem_size() to decouple get_kfunc_ptr_arg_type()
from register state.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 0f8d403083177..f6b373630ca95 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -10795,26 +10795,24 @@ static bool is_kfunc_rcu_protected(struct bpf_call_arg_meta *meta)
> }
>
> static bool is_kfunc_arg_mem_size(const struct btf *btf,
> - const struct btf_param *arg,
> - const struct bpf_reg_state *reg)
> + const struct btf_param *arg)
> {
> const struct btf_type *t;
>
> t = btf_type_skip_modifiers(btf, arg->type, NULL);
> - if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
> + if (!btf_type_is_scalar(t))
> return false;
>
> return btf_param_match_suffix(btf, arg, "__sz");
> }
[Severity: High]
Could this removal allow a pointer register to be incorrectly classified as a
memory size parameter during kfunc lookahead validation?
If a BPF program passes a pointer register (such as PTR_TO_STACK with a fixed
offset of 0) instead of a scalar to a size argument suffixed with __sz,
get_kfunc_ptr_arg_type() will now look ahead and classify it solely based on
the BTF signature.
Because of this classification, check_kfunc_args() eagerly processes the
unverified register as a size parameter before the generic scalar validation
loop catches it:
check_kfunc_args()
...
check_mem_size_reg(env, buff_reg, size_reg, argno, next_argno, ...)
Inside check_mem_size_reg(), the pointer with a fixed offset of 0 yields a
size of 0. Since zero-size is allowed, it passes the bounds check and calls
mark_chain_precision() on the pointer register.
Because mark_chain_precision() expects a SCALAR_VALUE, it triggers a
verifier_bug splat for backtracking misuse when given a pointer. On systems
configured with panic_on_warn, this creates a local denial of service
regression.
Should the register type check be retained here, or is there another way to
prevent pointer registers from bypassing validation during the lookahead?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=11
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state
2026-07-24 19:38 ` sashiko-bot
@ 2026-07-24 22:52 ` Amery Hung
0 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 22:52 UTC (permalink / raw)
To: sashiko-reviews; +Cc: bpf
On Fri, Jul 24, 2026 at 12:38 PM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Removing the `SCALAR_VALUE` check from `is_kfunc_arg_mem_size()` and `is_kfunc_arg_const_mem_size()` allows unvalidated pointer registers to be passed as memory size arguments during kfunc lookahead validation, triggering a `verifier_bug` kernel warning splat and local DoS.
> --
>
> --- Patch [11]: [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state ---
> commit 55013229ec44003dda12328783652d410aa4f75e
> Author: Amery Hung <ameryhung@gmail.com>
>
> bpf: Classify kfunc mem_size args from BTF without register state
>
> This commit drops the scalar register type check in is_kfunc_arg_mem_size()
> and is_kfunc_arg_const_mem_size() to decouple get_kfunc_ptr_arg_type()
> from register state.
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 0f8d403083177..f6b373630ca95 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -10795,26 +10795,24 @@ static bool is_kfunc_rcu_protected(struct bpf_call_arg_meta *meta)
> > }
> >
> > static bool is_kfunc_arg_mem_size(const struct btf *btf,
> > - const struct btf_param *arg,
> > - const struct bpf_reg_state *reg)
> > + const struct btf_param *arg)
> > {
> > const struct btf_type *t;
> >
> > t = btf_type_skip_modifiers(btf, arg->type, NULL);
> > - if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
> > + if (!btf_type_is_scalar(t))
> > return false;
> >
> > return btf_param_match_suffix(btf, arg, "__sz");
> > }
>
> [Severity: High]
> Could this removal allow a pointer register to be incorrectly classified as a
> memory size parameter during kfunc lookahead validation?
>
> If a BPF program passes a pointer register (such as PTR_TO_STACK with a fixed
> offset of 0) instead of a scalar to a size argument suffixed with __sz,
> get_kfunc_ptr_arg_type() will now look ahead and classify it solely based on
> the BTF signature.
>
> Because of this classification, check_kfunc_args() eagerly processes the
> unverified register as a size parameter before the generic scalar validation
> loop catches it:
>
> check_kfunc_args()
> ...
> check_mem_size_reg(env, buff_reg, size_reg, argno, next_argno, ...)
>
> Inside check_mem_size_reg(), the pointer with a fixed offset of 0 yields a
> size of 0. Since zero-size is allowed, it passes the bounds check and calls
> mark_chain_precision() on the pointer register.
>
> Because mark_chain_precision() expects a SCALAR_VALUE, it triggers a
> verifier_bug splat for backtracking misuse when given a pointer. On systems
> configured with panic_on_warn, this creates a local denial of service
> regression.
>
> Should the register type check be retained here, or is there another way to
> prevent pointer registers from bypassing validation during the lookahead?
I will change check_mem_size_reg() to be lookback by calling it in the
KF_ARG_MEM_SIZE case in patch 13 ("bpf: Distinguish fixed- and
variable-size kfunc mem args with MEM_FIXED_SIZE").
It is not bisect safe, but it will be free of this issue after patch
13 in the next respin.
>
> [ ... ]
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=11
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH bpf-next v2 12/18] bpf: Handle NULL kfunc pointer args without a KF_ARG_PTR_TO_NULL type
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (10 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 11/18] bpf: Classify kfunc mem_size args from BTF without register state Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 13/18] bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE Amery Hung
` (5 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
get_kfunc_ptr_arg_type() returned KF_ARG_PTR_TO_NULL when a nullable
pointer argument was passed a NULL register. This folded a register-state
decision (bpf_register_is_null()) into what is otherwise BTF-based
argument classification, and it short-circuited before the BTF_ID/MEM
resolution.
Drop KF_ARG_PTR_TO_NULL and handle the NULL case in check_kfunc_args()
instead: a nullable argument that is actually NULL is skipped. Note that
it is okay to skip even when it is a mem+size pair because the size
argument check has been moved to the scalar section. The skip is done
before get_kfunc_ptr_arg_type() so that a NULL passed to a nullable
non-scalar-struct argument is not newly rejected by the BTF_ID/MEM
resolution.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 37 +++++++++++++++----------------------
1 file changed, 15 insertions(+), 22 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f6b373630ca9..64668e7d184a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11082,7 +11082,6 @@ enum kfunc_ptr_arg_type {
KF_ARG_PTR_TO_CALLBACK,
KF_ARG_PTR_TO_RB_ROOT,
KF_ARG_PTR_TO_RB_NODE,
- KF_ARG_PTR_TO_NULL,
KF_ARG_PTR_TO_CONST_STR,
KF_ARG_CONST_MAP_PTR,
KF_ARG_PTR_TO_TIMER,
@@ -11359,11 +11358,6 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
meta->func_id == special_kfunc_list[KF_bpf_session_cookie])
return KF_ARG_PTR_TO_CTX;
- if (arg + 1 < nargs &&
- (is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
- is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1])))
- arg_mem_size = true;
-
/* 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
@@ -11372,10 +11366,6 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), arg))
return KF_ARG_PTR_TO_CTX;
- if (is_kfunc_arg_nullable(meta->btf, &args[arg]) && bpf_register_is_null(reg) &&
- !arg_mem_size)
- return KF_ARG_PTR_TO_NULL;
-
if (is_kfunc_arg_alloc_obj(meta->btf, &args[arg]))
return KF_ARG_PTR_TO_ALLOC_BTF_ID;
@@ -11437,6 +11427,11 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
return KF_ARG_PTR_TO_CALLBACK;
+ if (arg + 1 < nargs &&
+ (is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
+ is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1])))
+ arg_mem_size = true;
+
/* This is the catch all argument type of register types supported by
* check_helper_mem_access. However, we only allow when argument type is
* pointer to scalar, or struct composed (recursively) of scalars. When
@@ -12137,6 +12132,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ if (is_kfunc_arg_nullable(meta->btf, &args[i]) && bpf_register_is_null(reg))
+ continue;
+
kf_arg_type = get_kfunc_ptr_arg_type(env, regs, meta, t, ref_t, ref_tname,
args, i, nargs, argno, reg);
if (kf_arg_type < 0)
@@ -12149,8 +12147,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
switch (kf_arg_type) {
- case KF_ARG_PTR_TO_NULL:
- continue;
case KF_ARG_PTR_TO_ALLOC_BTF_ID:
case KF_ARG_PTR_TO_BTF_ID:
if (!is_trusted_reg(env, reg)) {
@@ -12414,19 +12410,16 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
case KF_ARG_PTR_TO_MEM_SIZE:
{
struct bpf_reg_state *buff_reg = reg;
- const struct btf_param *buff_arg = &args[i];
struct bpf_reg_state *size_reg = get_func_arg_reg(caller, regs, i + 1);
argno_t next_argno = argno_from_arg(i + 2);
- if (!bpf_register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) {
- ret = check_mem_size_reg(env, buff_reg, size_reg, argno, next_argno,
- BPF_READ | BPF_WRITE, true, meta);
- if (ret < 0) {
- verbose(env, "%s and ", reg_arg_name(env, argno));
- verbose(env, "%s memory, len pair leads to invalid memory access\n",
- reg_arg_name(env, next_argno));
- return ret;
- }
+ ret = check_mem_size_reg(env, buff_reg, size_reg, argno, next_argno,
+ BPF_READ | BPF_WRITE, true, meta);
+ if (ret < 0) {
+ verbose(env, "%s and ", reg_arg_name(env, argno));
+ verbose(env, "%s memory, len pair leads to invalid memory access\n",
+ reg_arg_name(env, next_argno));
+ return ret;
}
break;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 13/18] bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (11 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 12/18] bpf: Handle NULL kfunc pointer args without a KF_ARG_PTR_TO_NULL type Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case Amery Hung
` (4 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
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.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 59 ++++++++++++++++++++-----------------------
1 file changed, 28 insertions(+), 31 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 64668e7d184a..144c2d8aa492 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11078,7 +11078,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,
@@ -11344,7 +11343,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,
@@ -11444,7 +11443,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,
@@ -12146,7 +12145,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)) {
@@ -12169,7 +12168,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:
@@ -12200,7 +12198,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",
@@ -12396,33 +12394,32 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
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;
- }
- 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:
- {
- struct bpf_reg_state *buff_reg = reg;
- struct bpf_reg_state *size_reg = get_func_arg_reg(caller, regs, i + 1);
- argno_t next_argno = argno_from_arg(i + 2);
-
- ret = check_mem_size_reg(env, buff_reg, size_reg, argno, next_argno,
- BPF_READ | BPF_WRITE, true, meta);
- if (ret < 0) {
- verbose(env, "%s and ", reg_arg_name(env, argno));
- verbose(env, "%s memory, len pair leads to invalid memory access\n",
- reg_arg_name(env, next_argno));
- return ret;
+ 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;
+ } else {
+ struct bpf_reg_state *buff_reg = reg;
+ struct bpf_reg_state *size_reg = get_func_arg_reg(caller, regs, i + 1);
+ argno_t next_argno = argno_from_arg(i + 2);
+
+ ret = check_mem_size_reg(env, buff_reg, size_reg, argno, next_argno,
+ BPF_READ | BPF_WRITE, true, meta);
+ if (ret < 0) {
+ verbose(env, "%s and ", reg_arg_name(env, argno));
+ verbose(env, "%s memory, len pair leads to invalid memory access\n",
+ reg_arg_name(env, next_argno));
+ return ret;
+ }
}
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
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (12 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 13/18] bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:47 ` sashiko-bot
2026-07-24 19:08 ` [PATCH bpf-next v2 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register Amery Hung
` (3 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_func_arg() verified a variable-size memory argument across two
cases: ARG_PTR_TO_MEM was a no-op and the buffer+size pair was checked
only when the following ARG_MEM_SIZE argument was reached. Handle the
pair in the ARG_PTR_TO_MEM case instead, looking ahead to the size
register at regno + 1, mirroring the kfunc handling.
check_func_proto() already rejects a non-MEM_FIXED_SIZE ARG_PTR_TO_MEM
that is not paired with a following mem_size argument, so the size
register is guaranteed to exist and ARG_MEM_SIZE needs no case of its
own.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 144c2d8aa492..2326b12a1531 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8505,9 +8505,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
meta->subprogno = reg->subprogno;
break;
case ARG_PTR_TO_MEM:
- /* The access to this pointer is only checked when we hit the
- * next is_mem_size argument below.
- */
if (arg_type & MEM_FIXED_SIZE) {
err = check_mem_reg(env, reg, argno_from_reg(regno), fn->arg_size[arg],
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta);
@@ -8515,14 +8512,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
if (arg_type & MEM_ALIGNED)
err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
+ } else {
+ struct bpf_reg_state *size_reg = reg_state(env, regno + 1);
+
+ err = check_mem_size_reg(env, reg, size_reg, argno,
+ argno_from_reg(regno + 1),
+ arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
+ fn->arg_type[arg + 1] & SCALAR_MAYBE_ZERO, meta);
}
break;
- case ARG_MEM_SIZE:
- err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
- argno_from_reg(regno - 1), argno,
- fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
- arg_type & SCALAR_MAYBE_ZERO, meta);
- break;
case ARG_PTR_TO_DYNPTR:
err = process_dynptr_func(env, reg, argno, insn_idx, arg_type, &meta->ref_obj,
&meta->dynptr);
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case
2026-07-24 19:08 ` [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case Amery Hung
@ 2026-07-24 19:47 ` sashiko-bot
2026-07-24 21:10 ` Amery Hung
0 siblings, 1 reply; 30+ messages in thread
From: sashiko-bot @ 2026-07-24 19:47 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Calling check_mem_size_reg() on the size register before validating its type allows an unprivileged user to trigger a kernel warning (WARN_ONCE) and potential panic, leading to a local Denial of Service.
--
--- Patch [14]: [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case ---
commit 49ede5ce4d16f1df74560df7eef17176d13cb3e1
Author: Amery Hung <ameryhung@gmail.com>
bpf: Check helper mem+size in ARG_PTR_TO_MEM case
This commit modifies the BPF verifier to check variable-size memory arguments
and their associated size registers simultaneously during the ARG_PTR_TO_MEM
case, rather than waiting for the ARG_MEM_SIZE argument. It looks ahead to
regno + 1 to process the size register.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 144c2d8aa4922..2326b12a15313 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -8515,14 +8512,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> return err;
> if (arg_type & MEM_ALIGNED)
> err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
> + } else {
> + struct bpf_reg_state *size_reg = reg_state(env, regno + 1);
> +
> + err = check_mem_size_reg(env, reg, size_reg, argno,
> + argno_from_reg(regno + 1),
> + arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
> + fn->arg_type[arg + 1] & SCALAR_MAYBE_ZERO, meta);
[Severity: High]
Could this call to check_mem_size_reg() process a non-scalar register?
By looking ahead and processing size_reg at regno + 1 here, it seems this is
happening before the size register's type is validated in the next loop
iteration of check_func_arg().
If a BPF program passes a pointer (like PTR_TO_MAP_VALUE with var_off=0)
instead of a scalar for the size argument, it appears it would bypass the
scalar bounds checks. When check_mem_size_reg() subsequently calls
mark_chain_precision() on that non-scalar register, could this trigger a
verifier_bug() and BPF_WARN_ONCE()?
On systems with panic_on_warn enabled, this might lead to a local denial
of service.
> }
> break;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=14
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case
2026-07-24 19:47 ` sashiko-bot
@ 2026-07-24 21:10 ` Amery Hung
0 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 21:10 UTC (permalink / raw)
To: sashiko-reviews; +Cc: bpf
On Fri, Jul 24, 2026 at 12:47 PM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Calling check_mem_size_reg() on the size register before validating its type allows an unprivileged user to trigger a kernel warning (WARN_ONCE) and potential panic, leading to a local Denial of Service.
> --
>
> --- Patch [14]: [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case ---
> commit 49ede5ce4d16f1df74560df7eef17176d13cb3e1
> Author: Amery Hung <ameryhung@gmail.com>
>
> bpf: Check helper mem+size in ARG_PTR_TO_MEM case
>
> This commit modifies the BPF verifier to check variable-size memory arguments
> and their associated size registers simultaneously during the ARG_PTR_TO_MEM
> case, rather than waiting for the ARG_MEM_SIZE argument. It looks ahead to
> regno + 1 to process the size register.
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 144c2d8aa4922..2326b12a15313 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
> > @@ -8515,14 +8512,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > return err;
> > if (arg_type & MEM_ALIGNED)
> > err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
> > + } else {
> > + struct bpf_reg_state *size_reg = reg_state(env, regno + 1);
> > +
> > + err = check_mem_size_reg(env, reg, size_reg, argno,
> > + argno_from_reg(regno + 1),
> > + arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
> > + fn->arg_type[arg + 1] & SCALAR_MAYBE_ZERO, meta);
>
> [Severity: High]
> Could this call to check_mem_size_reg() process a non-scalar register?
>
> By looking ahead and processing size_reg at regno + 1 here, it seems this is
> happening before the size register's type is validated in the next loop
> iteration of check_func_arg().
>
This is a real issue.
I will drop patch 15, not moving helper check_mem_size_reg(). Then for
kfunc, defer check_mem_size_reg() in KF_ARG_MEM_SIZE and
KF_ARG_CONST_MEM_SIZE similar to helper.
> If a BPF program passes a pointer (like PTR_TO_MAP_VALUE with var_off=0)
> instead of a scalar for the size argument, it appears it would bypass the
> scalar bounds checks. When check_mem_size_reg() subsequently calls
> mark_chain_precision() on that non-scalar register, could this trigger a
> verifier_bug() and BPF_WARN_ONCE()?
>
> On systems with panic_on_warn enabled, this might lead to a local denial
> of service.
>
> > }
> > break;
>
> [ ... ]
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=14
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH bpf-next v2 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (13 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL Amery Hung
` (2 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
get_kfunc_ptr_arg_type() decided part of a kfunc pointer argument's type
from the caller's register: a PTR_TO_BTF_ID (or reg2btf_ids) register made
the argument KF_ARG_PTR_TO_BTF_ID, otherwise it fell through to a memory
buffer. Folding register state into argument classification prevents
describing a kfunc's arguments from its BTF alone, which is a prerequisite
for generating a helper-like prototype and eventually sharing the argument
checking (check_func_arg()) between helpers and kfuncs.
Classify pointer arguments from BTF only, and resolve them against the
register in check_kfunc_args():
- A pointer to a struct that is not paired with a __sz/__szk size
argument is classified KF_ARG_PTR_TO_BTF_ID and then checked against
the register. A register carrying a BTF ID (PTR_TO_BTF_ID or a
reg2btf_ids type) must be referenced or trusted and is matched against
the expected type. The only relaxation is when the struct is composed
of scalars, the register may be verified as a fixed-size memory buffer
sized from the BTF type; anything else is rejected.
- A pointer paired with a size argument is always a memory buffer and is
never classified as BTF_ID, so the __sz/__szk case no longer detours
through BTF_ID.
The referenced-or-trusted check thus moves into the KF_ARG_PTR_TO_BTF_ID
resolution, alongside the type match.
get_kfunc_ptr_arg_type() no longer needs the register, so drop its regs
and reg parameters; it is now a pure function of the kfunc's BTF.
When a register cannot satisfy a BTF_ID argument, report the register type
passed and, when the expected struct has a reg2btf_ids mapping, the
register type that would be accepted, instead of a confusing "socket".
Update the affected selftest messages accordingly.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 107 +++++++++++-------
.../selftests/bpf/progs/cgrp_kfunc_failure.c | 2 +-
.../selftests/bpf/progs/task_kfunc_failure.c | 2 +-
.../selftests/bpf/progs/verifier_vfs_reject.c | 6 +-
tools/testing/selftests/bpf/verifier/calls.c | 6 +-
5 files changed, 71 insertions(+), 52 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2326b12a1531..76a84574b23e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4924,6 +4924,19 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = {
[CONST_PTR_TO_MAP] = btf_bpf_map_id,
};
+static enum bpf_reg_type lookup_reg2btf_ids(const struct btf_type *ref_t)
+{
+ enum bpf_reg_type type;
+
+ for (type = 0; type < __BPF_REG_TYPE_MAX; type++) {
+ if (reg2btf_ids[type] &&
+ btf_type_by_id(btf_vmlinux, *reg2btf_ids[type]) == ref_t)
+ return type;
+ }
+
+ return NOT_INIT;
+}
+
static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg)
{
/* A referenced register is always trusted. */
@@ -11342,11 +11355,10 @@ 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_reg_state *regs, struct bpf_call_arg_meta *meta,
+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, struct bpf_reg_state *reg)
+ int arg, int nargs, argno_t argno)
{
bool arg_mem_size = false;
@@ -11411,16 +11423,6 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
if (is_kfunc_arg_res_spin_lock(meta->btf, &args[arg]))
return KF_ARG_PTR_TO_RES_SPIN_LOCK;
- if ((base_type(reg->type) == PTR_TO_BTF_ID || reg2btf_ids[base_type(reg->type)])) {
- if (!btf_type_is_struct(ref_t)) {
- verbose(env, "kernel function %s %s pointer type %s %s is not supported\n",
- meta->func_name, reg_arg_name(env, argno),
- btf_type_str(ref_t), ref_tname);
- return -EINVAL;
- }
- return KF_ARG_PTR_TO_BTF_ID;
- }
-
if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
return KF_ARG_PTR_TO_CALLBACK;
@@ -11429,10 +11431,14 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1])))
arg_mem_size = true;
- /* This is the catch all argument type of register types supported by
- * check_helper_mem_access. However, we only allow when argument type is
- * pointer to scalar, or struct composed (recursively) of scalars. When
- * arg_mem_size is true, the pointer can be void *.
+ /* A pointer to a struct without a size argument is classified as KF_ARG_PTR_TO_BTF_ID */
+ if (btf_type_is_struct(ref_t) && !arg_mem_size)
+ return KF_ARG_PTR_TO_BTF_ID;
+
+ /*
+ * Otherwise this is a memory buffer supported by check_helper_mem_access(): a pointer
+ * to a scalar, or to void when paired with a size argument. The access size is derived
+ * from the pointed-to BTF type unless a size argument follows.
*/
if (!btf_type_is_scalar(ref_t) && !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0) &&
(arg_mem_size ? !btf_type_is_void(ref_t) : 1)) {
@@ -12132,8 +12138,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (is_kfunc_arg_nullable(meta->btf, &args[i]) && bpf_register_is_null(reg))
continue;
- kf_arg_type = get_kfunc_ptr_arg_type(env, regs, meta, t, ref_t, ref_tname,
- args, i, nargs, argno, reg);
+ kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname,
+ args, i, nargs, argno);
if (kf_arg_type < 0)
return kf_arg_type;
@@ -12146,19 +12152,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
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)) {
- if (!is_kfunc_rcu(meta)) {
- verbose(env, "%s must be referenced or trusted\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (!is_rcu_reg(reg)) {
- verbose(env, "%s must be a rcu pointer\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- }
- fallthrough;
case KF_ARG_CONST_MAP_PTR:
case KF_ARG_PTR_TO_ITER:
case KF_ARG_PTR_TO_LIST_HEAD:
@@ -12377,20 +12370,46 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
break;
case KF_ARG_PTR_TO_BTF_ID:
/* Only base_type is checked, further checks are done here */
- if ((base_type(reg->type) != PTR_TO_BTF_ID ||
- (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) &&
- !reg2btf_ids[base_type(reg->type)]) {
- verbose(env, "%s is %s ", reg_arg_name(env, argno),
- reg_type_str(env, reg->type));
- verbose(env, "expected %s or socket\n",
- reg_type_str(env, base_type(reg->type) |
- (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
+ if (base_type(reg->type) == PTR_TO_BTF_ID ||
+ reg2btf_ids[base_type(reg->type)]) {
+ if (!is_trusted_reg(env, reg) ||
+ bpf_type_has_unsafe_modifiers(reg->type)) {
+ if (!is_kfunc_rcu(meta)) {
+ verbose(env, "%s must be referenced or trusted\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ if (!is_rcu_reg(reg)) {
+ verbose(env, "%s must be a rcu pointer\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ }
+
+ ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i, argno);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+
+ if (!__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0)) {
+ enum bpf_reg_type reg2btf_type = lookup_reg2btf_ids(ref_t);
+
+ verbose(env, "%s is %s expected %s %s",
+ reg_arg_name(env, argno), reg_type_str(env, reg->type),
+ btf_type_str(ref_t), ref_tname);
+ if (reg2btf_type != NOT_INIT)
+ verbose(env, " or %s", reg_type_str(env, reg2btf_type));
+ verbose(env, "\n");
return -EINVAL;
}
- ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i, argno);
- if (ret < 0)
- return ret;
- break;
+
+ /*
+ * If the register does not contain btf id but the argument type is a pointer to
+ * scalar-only struct, allow verifying it as a fixed size memory.
+ */
+ kf_arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
+ fallthrough;
case KF_ARG_PTR_TO_MEM:
if (kf_arg_type & MEM_FIXED_SIZE) {
resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
index d0d65d6d450c..efe7bcae70f8 100644
--- a/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
+++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
@@ -64,7 +64,7 @@ int BPF_PROG(cgrp_kfunc_acquire_no_null_check, struct cgroup *cgrp, const char *
}
SEC("tp_btf/cgroup_mkdir")
-__failure __msg("R1 pointer type STRUCT cgroup must point")
+__failure __msg("R1 is fp expected STRUCT cgroup")
int BPF_PROG(cgrp_kfunc_acquire_fp, struct cgroup *cgrp, const char *path)
{
struct cgroup *acquired, *stack_cgrp = (struct cgroup *)&path;
diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
index 8942b5478129..5c99b1e6532b 100644
--- a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
+++ b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
@@ -50,7 +50,7 @@ int BPF_PROG(task_kfunc_acquire_untrusted, struct task_struct *task, u64 clone_f
}
SEC("tp_btf/task_newtask")
-__failure __msg("R1 pointer type STRUCT task_struct must point")
+__failure __msg("R1 is fp expected STRUCT task_struct")
int BPF_PROG(task_kfunc_acquire_fp, struct task_struct *task, u64 clone_flags)
{
struct task_struct *acquired, *stack_task = (struct task_struct *)&clone_flags;
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
index 2870738d93f7..8f0c45421f89 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
@@ -28,7 +28,7 @@ int BPF_PROG(get_task_exe_file_kfunc_null)
}
SEC("lsm.s/inode_getxattr")
-__failure __msg("R1 pointer type STRUCT task_struct must point to scalar, or struct with scalar")
+__failure __msg("R1 is fp expected STRUCT task_struct")
int BPF_PROG(get_task_exe_file_kfunc_fp)
{
u64 x;
@@ -98,7 +98,7 @@ int BPF_PROG(path_d_path_kfunc_null)
}
SEC("lsm.s/task_alloc")
-__failure __msg("R1 must be referenced or trusted")
+__failure __msg("dereference of modified untrusted_ptr_")
int BPF_PROG(path_d_path_kfunc_untrusted_from_argument, struct task_struct *task)
{
struct path *root;
@@ -112,7 +112,7 @@ int BPF_PROG(path_d_path_kfunc_untrusted_from_argument, struct task_struct *task
}
SEC("lsm.s/file_open")
-__failure __msg("R1 must be referenced or trusted")
+__failure __msg("dereference of modified untrusted_ptr_")
int BPF_PROG(path_d_path_kfunc_untrusted_from_current)
{
struct path *pwd;
diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c
index 302d712e0d7e..8cd626e04551 100644
--- a/tools/testing/selftests/bpf/verifier/calls.c
+++ b/tools/testing/selftests/bpf/verifier/calls.c
@@ -31,7 +31,7 @@
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = REJECT,
- .errstr = "R1 pointer type STRUCT prog_test_fail1 must point to scalar",
+ .errstr = "R1 is fp expected STRUCT prog_test_fail1",
.fixup_kfunc_btf_id = {
{ "bpf_kfunc_call_test_fail1", 2 },
},
@@ -46,7 +46,7 @@
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = REJECT,
- .errstr = "max struct nesting depth exceeded\nR1 pointer type STRUCT prog_test_fail2",
+ .errstr = "max struct nesting depth exceeded\nR1 is fp expected STRUCT prog_test_fail2",
.fixup_kfunc_btf_id = {
{ "bpf_kfunc_call_test_fail2", 2 },
},
@@ -61,7 +61,7 @@
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = REJECT,
- .errstr = "R1 pointer type STRUCT prog_test_fail3 must point to scalar",
+ .errstr = "R1 is fp expected STRUCT prog_test_fail3",
.fixup_kfunc_btf_id = {
{ "bpf_kfunc_call_test_fail3", 2 },
},
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (14 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:38 ` sashiko-bot
2026-07-24 19:08 ` [PATCH bpf-next v2 17/18] bpf: Classify scalar kfunc arguments from BTF Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 18/18] bpf: Generate kfunc argument prototype at add-call time Amery Hung
17 siblings, 1 reply; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Now that get_kfunc_ptr_arg_type() classifies a kfunc pointer argument
from its BTF alone, express a nullable argument by OR-ing PTR_MAYBE_NULL
into the classified type, and resolve a NULL register after
classification instead of before it.
Previously check_kfunc_args() short-circuited a nullable argument passed
a NULL register with a continue placed before get_kfunc_ptr_arg_type(),
so the NULL never reached classification. That kept a register-state
decision (bpf_register_is_null()) ahead of the BTF-based classification.
This mirrors how helper arguments carry PTR_MAYBE_NULL in their
bpf_arg_type and is a step toward describing kfuncs with a bpf_func_proto:
the nullability now travels with the per-argument classification, so it is
captured when the prototype is generated at add-call time.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 156 ++++++++++++++++++++----------------------
1 file changed, 74 insertions(+), 82 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 76a84574b23e..3b357007e893 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11361,93 +11361,85 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *m
int arg, int nargs, argno_t argno)
{
bool arg_mem_size = false;
-
- if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] ||
- meta->func_id == special_kfunc_list[KF_bpf_session_is_return] ||
- meta->func_id == special_kfunc_list[KF_bpf_session_cookie])
- return KF_ARG_PTR_TO_CTX;
+ int arg_type;
/* 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
* arguments, we resolve it to a known kfunc_ptr_arg_type.
*/
- if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), arg))
- return KF_ARG_PTR_TO_CTX;
-
- if (is_kfunc_arg_alloc_obj(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_ALLOC_BTF_ID;
-
- if (is_kfunc_arg_refcounted_kptr(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_REFCOUNTED_KPTR;
-
- if (is_kfunc_arg_dynptr(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_DYNPTR;
-
- if (is_kfunc_arg_iter(meta, arg, &args[arg]))
- return KF_ARG_PTR_TO_ITER;
-
- if (is_kfunc_arg_list_head(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_LIST_HEAD;
-
- if (is_kfunc_arg_list_node(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_LIST_NODE;
-
- if (is_kfunc_arg_rbtree_root(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_RB_ROOT;
-
- if (is_kfunc_arg_rbtree_node(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_RB_NODE;
-
- if (is_kfunc_arg_const_str(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_CONST_STR;
-
- if (is_kfunc_arg_const_map(meta->btf, &args[arg]))
- return KF_ARG_CONST_MAP_PTR;
-
- if (is_kfunc_arg_map(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_BTF_ID;
-
- if (is_kfunc_arg_wq(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_WORKQUEUE;
-
- if (is_kfunc_arg_timer(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_TIMER;
-
- if (is_kfunc_arg_task_work(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_TASK_WORK;
-
- if (is_kfunc_arg_irq_flag(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_IRQ_FLAG;
-
- if (is_kfunc_arg_res_spin_lock(meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_RES_SPIN_LOCK;
-
- if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
- return KF_ARG_PTR_TO_CALLBACK;
-
- if (arg + 1 < nargs &&
- (is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
- is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1])))
- arg_mem_size = true;
+ if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] ||
+ meta->func_id == special_kfunc_list[KF_bpf_session_is_return] ||
+ meta->func_id == special_kfunc_list[KF_bpf_session_cookie])
+ arg_type = KF_ARG_PTR_TO_CTX;
+ else if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), arg))
+ arg_type = KF_ARG_PTR_TO_CTX;
+ else if (is_kfunc_arg_alloc_obj(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_ALLOC_BTF_ID;
+ else if (is_kfunc_arg_refcounted_kptr(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_REFCOUNTED_KPTR;
+ else if (is_kfunc_arg_dynptr(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_DYNPTR;
+ else if (is_kfunc_arg_iter(meta, arg, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_ITER;
+ else if (is_kfunc_arg_list_head(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_LIST_HEAD;
+ else if (is_kfunc_arg_list_node(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_LIST_NODE;
+ else if (is_kfunc_arg_rbtree_root(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_RB_ROOT;
+ else if (is_kfunc_arg_rbtree_node(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_RB_NODE;
+ else if (is_kfunc_arg_const_str(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_CONST_STR;
+ else if (is_kfunc_arg_const_map(meta->btf, &args[arg]))
+ arg_type = KF_ARG_CONST_MAP_PTR;
+ else if (is_kfunc_arg_map(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_BTF_ID;
+ else if (is_kfunc_arg_wq(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_WORKQUEUE;
+ else if (is_kfunc_arg_timer(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_TIMER;
+ else if (is_kfunc_arg_task_work(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_TASK_WORK;
+ else if (is_kfunc_arg_irq_flag(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_IRQ_FLAG;
+ else if (is_kfunc_arg_res_spin_lock(meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_RES_SPIN_LOCK;
+ else if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
+ arg_type = KF_ARG_PTR_TO_CALLBACK;
+ else {
+ if (arg + 1 < nargs &&
+ (is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
+ is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1])))
+ arg_mem_size = true;
+
+ if (btf_type_is_struct(ref_t) && !arg_mem_size) {
+ /* A pointer to a struct without a size argument is classified
+ * as KF_ARG_PTR_TO_BTF_ID.
+ */
+ arg_type = KF_ARG_PTR_TO_BTF_ID;
+ } else if (!btf_type_is_scalar(ref_t) &&
+ !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0) &&
+ (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) {
+ verbose(env, "%s pointer type %s %s must point to %sscalar, or struct with scalar\n",
+ reg_arg_name(env, argno),
+ btf_type_str(ref_t), ref_tname, arg_mem_size ? "void, " : "");
+ return -EINVAL;
+ } else {
+ /* Otherwise this is a memory buffer supported by
+ * check_helper_mem_access(): a pointer to a scalar, or to void
+ * when paired with a size argument. The access size is derived
+ * from the pointed-to BTF type unless a size argument follows.
+ */
+ arg_type = arg_mem_size ? KF_ARG_PTR_TO_MEM : KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
+ }
+ }
- /* A pointer to a struct without a size argument is classified as KF_ARG_PTR_TO_BTF_ID */
- if (btf_type_is_struct(ref_t) && !arg_mem_size)
- return KF_ARG_PTR_TO_BTF_ID;
+ if (is_kfunc_arg_nullable(meta->btf, &args[arg]))
+ arg_type |= PTR_MAYBE_NULL;
- /*
- * Otherwise this is a memory buffer supported by check_helper_mem_access(): a pointer
- * to a scalar, or to void when paired with a size argument. The access size is derived
- * from the pointed-to BTF type unless a size argument follows.
- */
- if (!btf_type_is_scalar(ref_t) && !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0) &&
- (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) {
- verbose(env, "%s pointer type %s %s must point to %sscalar, or struct with scalar\n",
- reg_arg_name(env, argno),
- btf_type_str(ref_t), ref_tname, arg_mem_size ? "void, " : "");
- return -EINVAL;
- }
- return arg_mem_size ? KF_ARG_PTR_TO_MEM : KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
+ return arg_type;
}
static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
@@ -12135,14 +12127,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
- if (is_kfunc_arg_nullable(meta->btf, &args[i]) && bpf_register_is_null(reg))
- continue;
-
kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname,
args, i, nargs, argno);
if (kf_arg_type < 0)
return kf_arg_type;
+ if (bpf_register_is_null(reg) && type_may_be_null(kf_arg_type))
+ continue;
+
if (is_kfunc_arg_map(btf, &args[i])) {
ref_id = *reg2btf_ids[CONST_PTR_TO_MAP];
ref_t = btf_type_by_id(btf_vmlinux, ref_id);
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH bpf-next v2 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL
2026-07-24 19:08 ` [PATCH bpf-next v2 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL Amery Hung
@ 2026-07-24 19:38 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-24 19:38 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Multi-line comments do not follow the BPF subsystem style.
--
--- Patch [16]: [PATCH bpf-next v2 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL ---
commit 8714272b2414d4acacf57ee10e018ba054ac3e95
Author: Amery Hung <ameryhung@gmail.com>
bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL
This commit updates get_kfunc_ptr_arg_type() to classify nullable kfunc
pointer arguments by OR-ing PTR_MAYBE_NULL into the returned type based
on BTF alone. It defers the check of whether the register is actually NULL
until after the classification is complete, matching how helper arguments
are handled.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 76a84574b23e9..3b357007e893b 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -11361,93 +11361,85 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *m
[ ... ]
> + if (btf_type_is_struct(ref_t) && !arg_mem_size) {
> + /* A pointer to a struct without a size argument is classified
> + * as KF_ARG_PTR_TO_BTF_ID.
> + */
[Severity: Low]
This isn't a bug, but does this multi-line comment follow the preferred BPF
subsystem style? Multi-line comments in this subsystem typically require the
opening /* on its own line before the text begins.
> + arg_type = KF_ARG_PTR_TO_BTF_ID;
> + } else if (!btf_type_is_scalar(ref_t) &&
> + !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0) &&
> + (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) {
> + verbose(env, "%s pointer type %s %s must point to %sscalar, or struct with scalar\n",
> + reg_arg_name(env, argno),
> + btf_type_str(ref_t), ref_tname, arg_mem_size ? "void, " : "");
> + return -EINVAL;
> + } else {
> + /* Otherwise this is a memory buffer supported by
> + * check_helper_mem_access(): a pointer to a scalar, or to void
> + * when paired with a size argument. The access size is derived
> + * from the pointed-to BTF type unless a size argument follows.
> + */
[Severity: Low]
This isn't a bug, but this comment has the same style issue where the text
starts on the same line as the opening /*.
> + arg_type = arg_mem_size ? KF_ARG_PTR_TO_MEM : KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
> + }
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724190813.1458271-1-ameryhung@gmail.com?part=16
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH bpf-next v2 17/18] bpf: Classify scalar kfunc arguments from BTF
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (15 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
2026-07-24 19:08 ` [PATCH bpf-next v2 18/18] bpf: Generate kfunc argument prototype at add-call time Amery Hung
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Add kfunc scalar argument types, classify them in get_kfunc_arg_type()
along side with pointer arguments and move scalar type verification
into the main switch in check_kfunc_args(). 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 | 118 +++++++++++++++++++++++++++---------------
1 file changed, 76 insertions(+), 42 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 3b357007e893..e44fc7296d0c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11099,6 +11099,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, /* const scalar */
+ 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 {
@@ -11355,14 +11360,40 @@ 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_param *args, int arg, int nargs)
{
+ const struct btf_type *t, *ref_t = NULL;
+ argno_t argno = argno_from_arg(arg + 1);
+ const char *ref_tname = NULL;
bool arg_mem_size = false;
int arg_type;
+ t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
+
+ /* 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;
+ 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;
+ }
+
+ ref_t = btf_type_skip_modifiers(meta->btf, t->type, NULL);
+ ref_tname = btf_name_by_offset(meta->btf, ref_t->name_off);
+
/* 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
@@ -12051,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])) {
@@ -12075,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));
@@ -12124,11 +12122,12 @@ 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, args, i, nargs);
if (kf_arg_type < 0)
return kf_arg_type;
@@ -12142,6 +12141,11 @@ 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:
+ case KF_ARG_CONST_MEM_SIZE:
+ case KF_ARG_MEM_SIZE:
+ case KF_ARG_ANYTHING:
+ case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
case KF_ARG_PTR_TO_ALLOC_BTF_ID:
case KF_ARG_PTR_TO_BTF_ID:
case KF_ARG_CONST_MAP_PTR:
@@ -12182,6 +12186,36 @@ 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:
+ 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;
+ break;
+ case KF_ARG_MEM_SIZE:
+ 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_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_PTR_TO_CTX:
if (reg->type != PTR_TO_CTX) {
verbose(env, "%s expected pointer to ctx, but got %s\n",
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH bpf-next v2 18/18] bpf: Generate kfunc argument prototype at add-call time
2026-07-24 19:07 [PATCH bpf-next v2 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (16 preceding siblings ...)
2026-07-24 19:08 ` [PATCH bpf-next v2 17/18] bpf: Classify scalar kfunc arguments from BTF Amery Hung
@ 2026-07-24 19:08 ` Amery Hung
17 siblings, 0 replies; 30+ messages in thread
From: Amery Hung @ 2026-07-24 19:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
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).
- Add a bpf_func_proto pointer to 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[]. It is
freed with the descriptor table in bpf_free_kfunc_desc_tab().
- 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 the target type (saved_dst_prog_type) is only
recorded later, in check_attach_btf_id(). Make resolve_prog_type() fall
back to prog->aux->dst_prog->type, which is set at load time and holds the
same value, so the resolved type is available at add-call time without
reordering verification passes. 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>
---
include/linux/bpf.h | 36 +++++------
include/linux/bpf_verifier.h | 18 +++++-
kernel/bpf/core.c | 4 +-
kernel/bpf/syscall.c | 2 +-
kernel/bpf/verifier.c | 112 ++++++++++++++++++++++++++++-------
5 files changed, 128 insertions(+), 44 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2cbfa033a4eb..ef172143b9c9 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -962,6 +962,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
@@ -986,7 +1001,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 {
@@ -996,7 +1011,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;
@@ -1004,7 +1019,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);
@@ -1194,21 +1209,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)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index b54c1a5c9b11..0655164e6af2 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1188,6 +1188,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
u32 btf_id,
struct bpf_attach_target_info *tgt_info);
void bpf_free_kfunc_btf_tab(struct bpf_kfunc_btf_tab *tab);
+void bpf_free_kfunc_desc_tab(struct bpf_kfunc_desc_tab *tab);
int mark_chain_precision(struct bpf_verifier_env *env, int regno);
@@ -1305,8 +1306,19 @@ static inline u32 type_flag(u32 type)
/* 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) ?
- prog->aux->saved_dst_prog_type : prog->type;
+ if (prog->type == BPF_PROG_TYPE_EXT) {
+ /*
+ * saved_dst_prog_type is only set once check_attach_btf_id()
+ * runs. Before that -- e.g. when generating kfunc prototypes at
+ * add-call time -- fall back to the attach target's type, which
+ * is available from load time and holds the same value.
+ */
+ if (prog->aux->saved_dst_prog_type)
+ return prog->aux->saved_dst_prog_type;
+ if (prog->aux->dst_prog)
+ return prog->aux->dst_prog->type;
+ }
+ return prog->type;
}
static inline bool bpf_prog_check_recur(const struct bpf_prog *prog)
@@ -1489,6 +1501,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 +1630,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;
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index e2076667b245..cd88772024a8 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -198,7 +198,9 @@ void bpf_prog_jit_attempt_done(struct bpf_prog *prog)
prog->aux->jited_linfo = NULL;
}
- kfree(prog->aux->kfunc_tab);
+#ifdef CONFIG_BPF_SYSCALL
+ bpf_free_kfunc_desc_tab(prog->aux->kfunc_tab);
+#endif
prog->aux->kfunc_tab = NULL;
}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 67704ddd29cb..0b5dc6788ba2 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2444,7 +2444,7 @@ static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)
module_put(prog->aux->mod);
kvfree(prog->aux->jited_linfo);
kvfree(prog->aux->linfo);
- kfree(prog->aux->kfunc_tab);
+ bpf_free_kfunc_desc_tab(prog->aux->kfunc_tab);
kfree(prog->aux->ctx_arg_info);
if (prog->aux->attach_btf)
btf_put(prog->aux->attach_btf);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e44fc7296d0c..1ca8779ab59a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2719,8 +2719,25 @@ 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);
+
+void bpf_free_kfunc_desc_tab(struct bpf_kfunc_desc_tab *tab)
+{
+ u32 i;
+
+ if (!tab)
+ return;
+ for (i = 0; i < tab->nr_descs; i++)
+ kfree(tab->descs[i].proto);
+ kfree(tab);
+}
+
int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
{
+ struct bpf_call_arg_meta meta;
+ struct bpf_func_proto *proto;
struct bpf_kfunc_btf_tab *btf_tab;
struct btf_func_model func_model;
struct bpf_kfunc_desc_tab *tab;
@@ -2806,11 +2823,29 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
if (err)
return err;
+ proto = kzalloc_obj(*proto, GFP_KERNEL_ACCOUNT);
+ if (!proto)
+ return -ENOMEM;
+
+ 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;
+
+ err = gen_kfunc_arg_proto(env, &meta, proto);
+ if (err) {
+ kfree(proto);
+ return err;
+ }
+
desc = &tab->descs[tab->nr_descs++];
desc->func_id = func_id;
desc->offset = offset;
desc->addr = addr;
desc->func_model = func_model;
+ desc->proto = proto;
sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
kfunc_desc_cmp_by_id_off, NULL);
return 0;
@@ -8352,9 +8387,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];
@@ -8858,7 +8893,7 @@ 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++) {
+ for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
if (!arg_type_is_raw_mem(fn->arg_type[i]))
continue;
if (meta->arg_raw_mem.regno)
@@ -8875,7 +8910,7 @@ static bool check_args_pair_invalid(const struct bpf_func_proto *fn, int arg)
bool has_size = fn->arg_size[arg] != 0;
bool is_next_size = false;
- if (arg + 1 < ARRAY_SIZE(fn->arg_type))
+ if (arg + 1 < MAX_BPF_FUNC_REG_ARGS)
is_next_size = arg_type_is_mem_size(fn->arg_type[arg + 1]);
if (base_type(fn->arg_type[arg]) != ARG_PTR_TO_MEM)
@@ -8906,7 +8941,7 @@ static bool check_btf_id_ok(const struct bpf_func_proto *fn)
{
int i;
- for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
+ for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
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)
@@ -8925,7 +8960,7 @@ static bool check_mem_arg_rw_flag_ok(const struct bpf_func_proto *fn)
{
int i;
- for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
+ for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
enum bpf_arg_type arg_type = fn->arg_type[i];
if (base_type(arg_type) != ARG_PTR_TO_MEM)
@@ -8941,7 +8976,7 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
{
int i;
- for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
+ for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
enum bpf_arg_type arg_type = fn->arg_type[i];
if (arg_type_is_release(arg_type)) {
@@ -10332,9 +10367,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;
}
@@ -11473,6 +11509,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,
@@ -12056,16 +12129,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)
@@ -12082,7 +12145,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 */
@@ -12127,9 +12190,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;
@@ -12682,7 +12742,7 @@ s64 bpf_helper_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn
size = fn->arg_size[arg];
goto out;
}
- if (arg + 1 < ARRAY_SIZE(fn->arg_type) &&
+ if (arg + 1 < MAX_BPF_FUNC_REG_ARGS &&
arg_type_is_mem_size(fn->arg_type[arg + 1])) {
int size_reg = BPF_REG_1 + arg + 1;
@@ -12981,6 +13041,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;
@@ -12997,6 +13058,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 &&
--
2.52.0
^ permalink raw reply related [flat|nested] 30+ messages in thread