* [PATCH bpf-next v1 01/18] bpf: Drop process_timer_func wrappers
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs Amery Hung
` (16 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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] 26+ messages in thread* [PATCH bpf-next v1 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 01/18] bpf: Drop process_timer_func wrappers Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:25 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 03/18] bpf: Split kfunc map argument into __const_map and __map Amery Hung
` (15 subsequent siblings)
17 siblings, 1 reply; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 | 103 ++++++++++++++++++++----------------------
1 file changed, 50 insertions(+), 53 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e8c76abbf76a..bff51aff2557 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8274,6 +8274,50 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
static bool can_elide_value_nullness(const struct bpf_map *map);
+/* Record the bpf_map argument of a helper or kfunc call in meta->map.
+ *
+ * When meta->map is already set from an earlier argument (a timer, workqueue or
+ * task_work object that is bound to a map), reject a map pointer that does not
+ * match it. That object sits in the register right before the map argument, and
+ * its type is taken from the bound map's record for the diagnostic.
+ */
+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)
+{
+ int map_regno = reg_from_argno(argno);
+
+ /* 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)) {
+ 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 R%d map_uid=%d doesn't match map pointer in R%d map_uid=%d\n",
+ obj_name, map_regno - 1, meta->map.uid, map_regno, 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 +8393,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 +12146,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] 26+ messages in thread* Re: [PATCH bpf-next v1 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs
2026-07-23 5:07 ` [PATCH bpf-next v1 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs Amery Hung
@ 2026-07-23 5:25 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-07-23 5:25 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] NULL pointer dereference in `process_map_ptr_arg` when a BPF function accepts multiple map arguments.
- [Low] Incorrect and misleading register numbers in `process_map_ptr_arg` verifier error message.
--
--- Patch [2]: [PATCH bpf-next v1 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs ---
commit e4d2232623c3b71f4ec65054cb7cebf7737a4120
Author: Amery Hung <ameryhung@gmail.com>
bpf: Unify const map ptr argument checking for helpers and kfuncs
This commit factors out the map pointer argument checking logic for both
helpers and kfuncs into a single process_map_ptr_arg() function. It uses
the bound map's btf_record to derive the object name (timer, workqueue,
or bpf_task_work) for diagnostic messages.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index e8c76abbf76a2..bff51aff25579 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -8274,6 +8274,50 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
>
> static bool can_elide_value_nullness(const struct bpf_map *map);
>
> +/* Record the bpf_map argument of a helper or kfunc call in meta->map.
> + *
> + * When meta->map is already set from an earlier argument (a timer, workqueue or
> + * task_work object that is bound to a map), reject a map pointer that does not
> + * match it. That object sits in the register right before the map argument, and
> + * its type is taken from the bound map's record for the diagnostic.
> + */
> +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)
> +{
> + int map_regno = reg_from_argno(argno);
[ ... ]
> + if (meta->map.ptr &&
> + (meta->map.ptr != reg->map_ptr || meta->map.uid != reg->map_uid)) {
> + struct btf_record *rec = meta->map.ptr->record;
> + const char *obj_name = "workqueue";
> +
> + if (rec->timer_off >= 0)
[Severity: Medium]
Will this cause a NULL pointer dereference in the verifier if a kfunc
accepts multiple map pointers?
If a custom kfunc or future helper takes two map pointers, such as
void *a__map and void *b__map, process_map_ptr_arg() will process the
second map and see meta->map.ptr already set by the first.
If the first map was a standard map without special fields, its record
pointer is NULL, causing rec->timer_off to crash.
Should we check if rec is NULL before accessing its fields?
> + obj_name = "timer";
> + else if (rec->task_work_off >= 0)
> + obj_name = "bpf_task_work";
> +
> + verbose(env, "%s pointer in R%d map_uid=%d doesn't match map pointer in R%d map_uid=%d\n",
> + obj_name, map_regno - 1, meta->map.uid, map_regno, reg->map_uid);
[Severity: Low]
Can the hardcoded map_regno - 1 produce confusing or negative register
numbers in the verifier error message?
If the map argument is passed on the stack (e.g. more than 5 arguments),
reg_from_argno() returns -1, making the printed registers -2 and -1.
Additionally, if a future kfunc separates the object and map arguments,
map_regno - 1 will point to the wrong register.
Could we retrieve the exact register or stack slot number for the
bound object dynamically instead of assuming strict adjacency?
> + return -EINVAL;
> + }
> +
> + meta->map.ptr = reg->map_ptr;
> + meta->map.uid = reg->map_uid;
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723050806.1158442-1-ameryhung@gmail.com?part=2
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH bpf-next v1 03/18] bpf: Split kfunc map argument into __const_map and __map
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 01/18] bpf: Drop process_timer_func wrappers Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 02/18] bpf: Unify const map ptr argument checking for helpers and kfuncs Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:35 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 04/18] bpf: Pass kfunc meta to mem and mem_size check Amery Hung
` (14 subsequent siblings)
17 siblings, 1 reply; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 bff51aff2557..0ba0de8eb8fe 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10823,6 +10823,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");
@@ -11069,7 +11074,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,
@@ -11388,8 +11393,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;
@@ -12137,19 +12145,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)) {
@@ -12165,6 +12169,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:
@@ -12371,12 +12376,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 (!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;
+ 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] 26+ messages in thread* Re: [PATCH bpf-next v1 03/18] bpf: Split kfunc map argument into __const_map and __map
2026-07-23 5:07 ` [PATCH bpf-next v1 03/18] bpf: Split kfunc map argument into __const_map and __map Amery Hung
@ 2026-07-23 5:35 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-07-23 5:35 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The newly introduced `KF_ARG_CONST_MAP_PTR` logic fails to verify that the register's base type is `CONST_PTR_TO_MAP`. Any register with a non-zero union aliasing `map_ptr` (such as `PTR_TO_MAP_VALUE`) can bypass validation, leading to severe type confusion at runtime where user-controlled map value memory is treated as a `struct bpf_map *`.
--
--- Patch [3]: [PATCH bpf-next v1 03/18] bpf: Split kfunc map argument into __const_map and __map ---
commit 2aa2cd575b3d4be283e92e30e814910f54fbe0cc
Author: Amery Hung <ameryhung@gmail.com>
bpf: Split kfunc map argument into __const_map and __map
This commit splits the kfunc map argument annotation to avoid type confusion.
__const_map indicates a verifier-known map, while __map indicates an opaque
struct bpf_map resolved at runtime.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index bff51aff25579..0ba0de8eb8fe0 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -12164,6 +12168,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:
[Severity: Critical]
Does this skip the is_trusted_reg() checks and leave arg_type as ARG_DONTCARE?
Since KF_ARG_CONST_MAP_PTR falls through to the break below, it seems we might
bypass pointer type safety enforcement here.
[ ... ]
> @@ -12371,12 +12376,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 (!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;
> + break;
[Severity: Critical]
Is there a missing check for base_type(reg->type) == CONST_PTR_TO_MAP here?
Since this breaks instead of falling through to KF_ARG_PTR_TO_BTF_ID like the
old code did, it looks like any register with a non-zero union aliasing
map_ptr (such as PTR_TO_MAP_VALUE) can bypass validation.
For example, could a BPF program call bpf_wq_init(wq, map__const_map, flags)
and pass a PTR_TO_MAP_VALUE for both the wq and map__const_map arguments?
If wq and map__const_map use the same register, process_map_ptr_arg() would
verify meta->map.ptr == reg->map_ptr (which evaluates to true), and then break
out without ever verifying the register type. This would cause the kernel to
treat user-controlled bytes inside a map value as a kernel struct bpf_map
object, potentially leading to memory corruption.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723050806.1158442-1-ameryhung@gmail.com?part=3
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH bpf-next v1 04/18] bpf: Pass kfunc meta to mem and mem_size check
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (2 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 03/18] bpf: Split kfunc map argument into __const_map and __map Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 05/18] bpf: Check helper and kfunc mem+size arguments identically Amery Hung
` (13 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 0ba0de8eb8fe..29d45636c28b 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;
@@ -9264,7 +9262,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",
@@ -12410,7 +12408,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;
@@ -12424,7 +12422,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] 26+ messages in thread* [PATCH bpf-next v1 05/18] bpf: Check helper and kfunc mem+size arguments identically
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (3 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 04/18] bpf: Pass kfunc meta to mem and mem_size check Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:52 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments Amery Hung
` (12 subsequent siblings)
17 siblings, 1 reply; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 29d45636c28b..6520742d51b5 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),
@@ -12421,8 +12416,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] 26+ messages in thread* Re: [PATCH bpf-next v1 05/18] bpf: Check helper and kfunc mem+size arguments identically
2026-07-23 5:07 ` [PATCH bpf-next v1 05/18] bpf: Check helper and kfunc mem+size arguments identically Amery Hung
@ 2026-07-23 5:52 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-07-23 5:52 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Unchecked nullable pointers passed to helpers with `size > 0` can cause a kernel panic, as the verifier does not enforce a strict correlation between the pointer's runtime NULL state and a zero-size constraint.
--
--- Patch [5]: [PATCH bpf-next v1 05/18] bpf: Check helper and kfunc mem+size arguments identically ---
commit 209f89ec76658f4c319cb2f0b37557c6e39026aa
Author: Amery Hung <ameryhung@gmail.com>
bpf: Check helper and kfunc mem+size arguments identically
This patch unifies the memory and size argument checking between helpers
and kfuncs. By moving the may_be_null handling into check_mem_size_reg(),
it prevents an issue where an un-narrowed map-of-maps value could be
passed to a nullable helper memory argument and leak kernel pointers.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 29d45636c28b8..6520742d51b58 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);
> + }
[ ... ]
> - 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;
> }
[Severity: High]
This is a pre-existing issue, but by clearing the PTR_MAYBE_NULL flag
temporarily, does this bypass the verifier's check to ensure the size
evaluates to 0 when the pointer is NULL at runtime?
If a BPF program obtains a nullable pointer (for instance, from
bpf_map_lookup_elem) and passes it to a helper with a size greater than 0
without a NULL check, the verifier permits this because it verifies the
bounds as if the pointer is valid.
At runtime, if the pointer is NULL, wouldn't the helper execute and
dereference the NULL pointer? For example, in net/core/filter.c:bpf_csum_diff(),
if from_size or to_size > 0, the pointer is dereferenced by csum_partial():
if (from_size && to_size)
ret = csum_sub(csum_partial(to, to_size, ret),
csum_partial(from, from_size, 0));
else if (to_size)
ret = csum_partial(to, to_size, ret);
else if (from_size)
ret = ~csum_partial(from, from_size, ~ret);
Similarly, in kernel/bpf/helpers.c:bpf_snprintf(), bstr_printf() could be
called without checking if str is NULL when str_size > 0:
err = bstr_printf(str, str_size, fmt, data.bin_args);
Should the verifier emit a runtime check or enforce a constraint to ensure
the size is 0 if a nullable pointer is passed to prevent a kernel panic?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723050806.1158442-1-ameryhung@gmail.com?part=5
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH bpf-next v1 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (4 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 05/18] bpf: Check helper and kfunc mem+size arguments identically Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:42 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way Amery Hung
` (11 subsequent siblings)
17 siblings, 1 reply; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5:07 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..78a9ad22b10d
--- /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
+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, 8);
+ 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
+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] 26+ messages in thread* Re: [PATCH bpf-next v1 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments
2026-07-23 5:07 ` [PATCH bpf-next v1 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments Amery Hung
@ 2026-07-23 5:42 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-07-23 5:42 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The selftest `mapofmaps_value_as_kfunc_mem_buf` uses an out-of-bounds buffer size (8 instead of 4), and both tests lack explicit `__msg(...)` assertions. This causes the test to pass (fail verification) for the wrong reason, rendering it ineffective as a regression guard.
--
--- Patch [6]: [PATCH bpf-next v1 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments ---
commit 967fe45d86aee49807b5742ffece3f858b20a4fd
Author: Amery Hung <ameryhung@gmail.com>
selftests/bpf: Add tests for helper and kfunc mem+size arguments
Make sure the return of map-of-maps lookup cannot be passed to nullable
memory buffer argument for helper and kfunc.
> 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 0000000000000..78a9ad22b10df
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/verifier_mem_size_reg.c
[ ... ]
> +/* An un-narrowed map-of-maps value must be rejected as a __nullable kfunc mem buffer */
> +SEC("?tc")
> +__failure
> +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, 8);
[Severity: Medium]
Does using a size of 8 here exceed the outer map's value size of 4?
If the intended type check is missing, the verifier will still reject the
program due to a bounds check failure (size 8 > 4). Since the test uses
__failure without __msg(...), this unrelated bounds-check failure satisfies
the test expectation and causes it to pass for the wrong reason.
> + 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
[Severity: Medium]
Similar to the above, would it be better to include a __msg(...) assertion
here?
Without an explicit message check, this test could pass due to unrelated
verifier errors.
> +int mapofmaps_value_as_helper_mem_buf(struct __sk_buff *skb)
> +{
> + __u32 key = 0;
> + void *inner;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723050806.1158442-1-ameryhung@gmail.com?part=6
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH bpf-next v1 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (5 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 06/18] selftests/bpf: Add tests for helper and kfunc mem+size arguments Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:57 ` sashiko-bot
2026-07-23 5:07 ` [PATCH bpf-next v1 08/18] bpf: Express ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE | SCALAR_MAYBE_ZERO Amery Hung
` (10 subsequent siblings)
17 siblings, 1 reply; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5:07 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 6520742d51b5..be5d93c0da81 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;
@@ -8483,9 +8488,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)
@@ -9257,7 +9261,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",
@@ -12403,7 +12407,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] 26+ messages in thread* Re: [PATCH bpf-next v1 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way
2026-07-23 5:07 ` [PATCH bpf-next v1 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way Amery Hung
@ 2026-07-23 5:57 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-07-23 5:57 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Global subprograms incorrectly accept NULL pointers for non-nullable ARG_PTR_TO_MEM arguments, leading to kernel NULL pointer dereference.
--
--- Patch [7]: [PATCH bpf-next v1 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way ---
commit 878ed5f4f87b10a9bf04aa450c46f6f99550d430
Author: Amery Hung <ameryhung@gmail.com>
bpf: Check fixed-size mem args of helpers and kfuncs the same way
This commit consolidates the fixed-size memory argument checking for helpers,
kfuncs, and global subprograms by routing the helper MEM_FIXED_SIZE case
through check_mem_reg. It also resolves a bug where passing a NULL to an
argument marked PTR_MAYBE_NULL | ARG_PTR_TO_FIXED_SIZE_MEM would falsely
reject the program.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 6520742d51b58..be5d93c0da813 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;
[Severity: High]
This isn't a bug introduced by this patch, but does this early return allow a
NULL pointer to bypass memory access bounds checks and type validations when
the register is a constant 0?
[ ... ]
> @@ -9257,7 +9261,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",
[Severity: High]
This is a pre-existing issue, but if a caller passes a hardcoded NULL
(SCALAR_VALUE of 0) to a global subprogram expecting a non-nullable
ARG_PTR_TO_MEM, check_mem_reg returns 0 immediately.
Because the SCALAR_VALUE type does not have the PTR_MAYBE_NULL flag set,
this subsequent bitwise check in btf_check_func_arg_match:
!(arg->arg_type & PTR_MAYBE_NULL) && (reg->type & PTR_MAYBE_NULL)
will fail to catch the NULL. Could this allow a global subprogram to
incorrectly accept and dereference a NULL pointer at runtime, resulting
in a kernel panic?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723050806.1158442-1-ameryhung@gmail.com?part=7
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH bpf-next v1 08/18] bpf: Express ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE | SCALAR_MAYBE_ZERO
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (6 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 07/18] bpf: Check fixed-size mem args of helpers and kfuncs the same way Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 09/18] bpf: Rename ARG_CONST_SIZE{,_OR_ZERO} to ARG_MEM_SIZE{,_OR_ZERO} Amery Hung
` (9 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5:07 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 d9542127dfdf..a7b3bb56c70b 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 be5d93c0da81..5e09b95458ba 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,
@@ -8500,13 +8498,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] 26+ messages in thread* [PATCH bpf-next v1 09/18] bpf: Rename ARG_CONST_SIZE{,_OR_ZERO} to ARG_MEM_SIZE{,_OR_ZERO}
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (7 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 08/18] bpf: Express ARG_CONST_SIZE_OR_ZERO as ARG_CONST_SIZE | SCALAR_MAYBE_ZERO Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 10/18] bpf: Fold __szk const size handling into the scalar arg path Amery Hung
` (8 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5:07 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 a7b3bb56c70b..4fd360228321 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 cbb1e49b9bcb..a23a42cbe3b1 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8707,7 +8707,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 5e09b95458ba..7698ead36778 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,
@@ -8494,7 +8494,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] 26+ messages in thread* [PATCH bpf-next v1 10/18] bpf: Fold __szk const size handling into the scalar arg path
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (8 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 09/18] bpf: Rename ARG_CONST_SIZE{,_OR_ZERO} to ARG_MEM_SIZE{,_OR_ZERO} Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:07 ` [PATCH bpf-next v1 11/18] bpf: Classify kfunc mem_size args from BTF without register state Amery Hung
` (7 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5:07 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 7698ead36778..83f7afb86dfe 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),
@@ -12070,24 +12099,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;
@@ -12408,7 +12424,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)) {
@@ -12421,23 +12436,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] 26+ messages in thread* [PATCH bpf-next v1 11/18] bpf: Classify kfunc mem_size args from BTF without register state
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (9 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 10/18] bpf: Fold __szk const size handling into the scalar arg path Amery Hung
@ 2026-07-23 5:07 ` Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 12/18] bpf: Handle NULL kfunc pointer args without a KF_ARG_PTR_TO_NULL type Amery Hung
` (6 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5:07 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 83f7afb86dfe..d77b590e0469 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10801,26 +10801,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");
@@ -11354,7 +11352,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,
@@ -11368,8 +11366,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,
@@ -12100,7 +12098,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;
@@ -12145,7 +12143,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] 26+ messages in thread* [PATCH bpf-next v1 12/18] bpf: Handle NULL kfunc pointer args without a KF_ARG_PTR_TO_NULL type
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (10 preceding siblings ...)
2026-07-23 5:07 ` [PATCH bpf-next v1 11/18] bpf: Classify kfunc mem_size args from BTF without register state Amery Hung
@ 2026-07-23 5:08 ` Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 13/18] bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE Amery Hung
` (5 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 d77b590e0469..438c27d9b39e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11088,7 +11088,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,
@@ -11365,11 +11364,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
@@ -11378,10 +11372,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;
@@ -11443,6 +11433,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
@@ -12143,6 +12138,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)
@@ -12155,8 +12153,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)) {
@@ -12420,19 +12416,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] 26+ messages in thread* [PATCH bpf-next v1 13/18] bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (11 preceding siblings ...)
2026-07-23 5:08 ` [PATCH bpf-next v1 12/18] bpf: Handle NULL kfunc pointer args without a KF_ARG_PTR_TO_NULL type Amery Hung
@ 2026-07-23 5:08 ` Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case Amery Hung
` (4 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 438c27d9b39e..f89551bbae7c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11084,7 +11084,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,
@@ -11350,7 +11349,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,
@@ -11450,7 +11449,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,
@@ -12152,7 +12151,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)) {
@@ -12175,7 +12174,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:
@@ -12206,7 +12204,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",
@@ -12402,33 +12400,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] 26+ messages in thread* [PATCH bpf-next v1 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (12 preceding siblings ...)
2026-07-23 5:08 ` [PATCH bpf-next v1 13/18] bpf: Distinguish fixed- and variable-size kfunc mem args with MEM_FIXED_SIZE Amery Hung
@ 2026-07-23 5:08 ` Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register Amery Hung
` (3 subsequent siblings)
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 f89551bbae7c..65de60301861 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8511,9 +8511,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);
@@ -8521,14 +8518,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] 26+ messages in thread* [PATCH bpf-next v1 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (13 preceding siblings ...)
2026-07-23 5:08 ` [PATCH bpf-next v1 14/18] bpf: Check helper mem+size in ARG_PTR_TO_MEM case Amery Hung
@ 2026-07-23 5:08 ` Amery Hung
2026-07-23 7:27 ` sashiko-bot
2026-07-23 5:08 ` [PATCH bpf-next v1 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL Amery Hung
` (2 subsequent siblings)
17 siblings, 1 reply; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 65de60301861..aa3313aeafdb 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. */
@@ -11348,11 +11361,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;
@@ -11417,16 +11429,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;
@@ -11435,10 +11437,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)) {
@@ -12138,8 +12144,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;
@@ -12152,19 +12158,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:
@@ -12383,20 +12376,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)) {
+ 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] 26+ messages in thread* Re: [PATCH bpf-next v1 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register
2026-07-23 5:08 ` [PATCH bpf-next v1 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register Amery Hung
@ 2026-07-23 7:27 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-07-23 7:27 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The removal of the `bpf_type_has_unsafe_modifiers()` check allows referenced pointers with unsafe modifiers (specifically `MEM_PERCPU`) to be passed as normal pointers to regular kfuncs.
--
--- Patch [15]: [PATCH bpf-next v1 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register ---
commit 23915f0b14432c05d0cd9201ab12d531e9b8df8a
Author: Amery Hung <ameryhung@gmail.com>
Subject: bpf: Classify kfunc pointer arguments from BTF, resolve type against the register
This commit refactors kfunc pointer argument classification to rely on BTF
rather than the caller's register state. It also moves the referenced-or-trusted
checks for BTF ID arguments into the BTF ID resolution path.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -12383,20 +12376,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)) {
> + 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);
[Severity: Critical]
Does this new structure allow referenced pointers with unsafe modifiers (like
MEM_PERCPU) to bypass validation and be passed to regular kfuncs?
The original code explicitly checked for unsafe modifiers:
bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg)
However, is_trusted_reg() unconditionally returns true for all referenced
pointers. If a BPF program allocates a per-CPU object using
bpf_percpu_obj_new(), it receives a referenced pointer with the
PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU type.
Since is_trusted_reg() will trust this referenced pointer, the unsafe
modifier check appears to be bypassed. If this pointer is then passed to a
generic kfunc expecting a standard kernel pointer, could dereferencing the
raw per-CPU pointer (which acts as an offset rather than a virtual address)
trigger a page fault and crash the kernel?
> + if (ret < 0)
> + return ret;
> + break;
> + }
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723050806.1158442-1-ameryhung@gmail.com?part=15
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH bpf-next v1 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (14 preceding siblings ...)
2026-07-23 5:08 ` [PATCH bpf-next v1 15/18] bpf: Classify kfunc pointer arguments from BTF, resolve type against the register Amery Hung
@ 2026-07-23 5:08 ` Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF Amery Hung
2026-07-23 5:08 ` [PATCH bpf-next v1 18/18] bpf: Generate kfunc argument prototype at add-call time Amery Hung
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 aa3313aeafdb..24858a38c34c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11367,93 +11367,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,
@@ -12141,14 +12133,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] 26+ messages in thread* [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (15 preceding siblings ...)
2026-07-23 5:08 ` [PATCH bpf-next v1 16/18] bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL Amery Hung
@ 2026-07-23 5:08 ` Amery Hung
2026-07-23 8:01 ` sashiko-bot
2026-07-23 5:08 ` [PATCH bpf-next v1 18/18] bpf: Generate kfunc argument prototype at add-call time Amery Hung
17 siblings, 1 reply; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5:08 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Pointer kfunc arguments are classified into a kfunc_ptr_arg_type by
get_kfunc_ptr_arg_type() from the BTF alone, then validated against the
register state in check_kfunc_args(). Scalar arguments, in contrast,
were special-cased in a block before the switch: the SCALAR_VALUE
check, the __k constant / __sz mem-size handling, and the rdonly/rdwr
return-buffer sizing were all done inline.
Give scalars the same treatment. Add kfunc_ptr_arg_type values for the
scalar kinds -- KF_ARG_CONST_BTF_ID (__k), KF_ARG_CONST_MEM_SIZE and
KF_ARG_MEM_SIZE (__sz), KF_ARG_CONST_ALLOC_SIZE_OR_ZERO (rdonly/rdwr
buffer size), and KF_ARG_ANYTHING -- and classify them from the BTF
suffix/name in get_kfunc_arg_type() (renamed from
get_kfunc_ptr_arg_type() now that it handles all argument kinds).
The register-state validation for each scalar kind moves into its own
case in the check_kfunc_args() switch, alongside the pointer cases. The
pointer-only preparation (NULL check and ref_t/ref_tname resolution) is
now guarded by btf_type_is_ptr().
This keeps BTF-based classification separate from register validation
for every argument, paving the way for generating the kfunc argument
prototype at add-call time. No functional change intended.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 124 ++++++++++++++++++++++++++++--------------
1 file changed, 82 insertions(+), 42 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 24858a38c34c..ba2608592bd5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11105,6 +11105,11 @@ enum kfunc_ptr_arg_type {
KF_ARG_PTR_TO_IRQ_FLAG,
KF_ARG_PTR_TO_RES_SPIN_LOCK,
KF_ARG_PTR_TO_TASK_WORK,
+ KF_ARG_CONST_BTF_ID, /* const scalar carrying a BTF type id */
+ KF_ARG_CONST_MEM_SIZE, /* const scalar sizing a preceding memory arg */
+ KF_ARG_CONST_ALLOC_SIZE_OR_ZERO, /* const scalar sizing a returned buffer */
+ KF_ARG_MEM_SIZE, /* scalar sizing a preceding memory arg */
+ KF_ARG_ANYTHING, /* unconstrained scalar */
};
enum special_kfunc_type {
@@ -11361,14 +11366,34 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
}
static int
-get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- const struct btf_type *t, const struct btf_type *ref_t,
- const char *ref_tname, const struct btf_param *args,
- int arg, int nargs, argno_t argno)
+get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
+ const struct btf_type *t, const struct btf_type *ref_t,
+ const char *ref_tname, const struct btf_param *args,
+ int arg, int nargs, argno_t argno)
{
bool arg_mem_size = false;
int arg_type;
+ /* Scalar arguments are classified from their BTF suffix/name alone. */
+ if (btf_type_is_scalar(t)) {
+ if (is_kfunc_arg_constant(meta->btf, &args[arg]))
+ return KF_ARG_CONST_BTF_ID;
+ if (is_kfunc_arg_const_mem_size(meta->btf, &args[arg]))
+ return KF_ARG_CONST_MEM_SIZE;
+ if (is_kfunc_arg_mem_size(meta->btf, &args[arg]))
+ return KF_ARG_MEM_SIZE;
+ if (is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdonly_buf_size") ||
+ is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdwr_buf_size"))
+ return KF_ARG_CONST_ALLOC_SIZE_OR_ZERO;
+ return KF_ARG_ANYTHING;
+ }
+
+ if (!btf_type_is_ptr(t)) {
+ verbose(env, "Unrecognized %s type %s\n",
+ reg_arg_name(env, argno), btf_type_str(t));
+ return -EINVAL;
+ }
+
/* In this function, we verify the kfunc's BTF as per the argument type,
* leaving the rest of the verification with respect to the register
* type to our caller. When a set of conditions hold in the BTF type of
@@ -12057,7 +12082,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
int regno = reg_from_argno(argno);
bool btf_id_fixed_off_ok = true;
u32 ref_id, type_size;
- bool is_ret_buf_sz = false;
int kf_arg_type;
if (is_kfunc_arg_prog_aux(btf, &args[i])) {
@@ -12081,39 +12105,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
t = btf_type_skip_modifiers(btf, args[i].type, NULL);
- if (btf_type_is_scalar(t)) {
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- return -EINVAL;
- }
-
- if (is_kfunc_arg_constant(meta->btf, &args[i]) ||
- is_kfunc_arg_const_mem_size(meta->btf, &args[i])) {
- ret = process_const_arg(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- } else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
- meta->r0_rdonly = true;
- is_ret_buf_sz = true;
- } else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) {
- is_ret_buf_sz = true;
- }
-
- if (is_ret_buf_sz) {
- ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
- if (ret < 0)
- return ret;
- }
- continue;
- }
-
- if (!btf_type_is_ptr(t)) {
- verbose(env, "Unrecognized %s type %s\n",
- reg_arg_name(env, argno), btf_type_str(t));
- return -EINVAL;
- }
-
- if ((bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
+ if (btf_type_is_ptr(t) && (bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
!is_kfunc_arg_nullable(meta->btf, &args[i])) {
verbose(env, "Possibly NULL pointer passed to trusted %s\n",
reg_arg_name(env, argno));
@@ -12130,11 +12122,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (reg_is_referenced(env, reg))
update_ref_obj(&meta->ref_obj, reg);
- ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ if (btf_type_is_ptr(t)) {
+ ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
+ ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ }
- kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname,
- args, i, nargs, argno);
+ kf_arg_type = get_kfunc_arg_type(env, meta, t, ref_t, ref_tname,
+ args, i, nargs, argno);
if (kf_arg_type < 0)
return kf_arg_type;
@@ -12148,6 +12142,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
switch (base_type(kf_arg_type)) {
+ case KF_ARG_CONST_BTF_ID:
+ case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ case KF_ARG_ANYTHING:
case KF_ARG_PTR_TO_ALLOC_BTF_ID:
case KF_ARG_PTR_TO_BTF_ID:
case KF_ARG_CONST_MAP_PTR:
@@ -12157,6 +12154,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
case KF_ARG_PTR_TO_RB_ROOT:
case KF_ARG_PTR_TO_RB_NODE:
case KF_ARG_PTR_TO_MEM:
+ case KF_ARG_CONST_MEM_SIZE:
+ case KF_ARG_MEM_SIZE:
case KF_ARG_PTR_TO_CALLBACK:
case KF_ARG_PTR_TO_CONST_STR:
case KF_ARG_PTR_TO_WORKQUEUE:
@@ -12188,6 +12187,34 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
switch (base_type(kf_arg_type)) {
+ case KF_ARG_CONST_BTF_ID:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ ret = process_const_arg(env, reg, argno, meta);
+ if (ret < 0)
+ return ret;
+ break;
+ case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size"))
+ meta->r0_rdonly = true;
+ ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
+ if (ret < 0)
+ return ret;
+ break;
+ case KF_ARG_ANYTHING:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ break;
case KF_ARG_PTR_TO_CTX:
if (reg->type != PTR_TO_CTX) {
verbose(env, "%s expected pointer to ctx, but got %s\n",
@@ -12435,6 +12462,19 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
}
break;
+ case KF_ARG_CONST_MEM_SIZE:
+ if (reg->type != SCALAR_VALUE) {
+ verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ ret = process_const_arg(env, reg, argno, meta);
+ if (ret < 0)
+ return ret;
+
+ fallthrough;
+ case KF_ARG_MEM_SIZE:
+ break;
case KF_ARG_PTR_TO_CALLBACK:
if (reg->type != PTR_TO_FUNC) {
verbose(env, "%s expected pointer to func\n", reg_arg_name(env, argno));
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF
2026-07-23 5:08 ` [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF Amery Hung
@ 2026-07-23 8:01 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-07-23 8:01 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] The `KF_ARG_MEM_SIZE` argument type completely bypasses the `SCALAR_VALUE` check, allowing eBPF programs to pass massive pointers as buffer sizes.
- [Low] Uninitialized variables `ref_t` and `ref_tname` passed by value to `get_kfunc_arg_type()`.
--
--- Patch [17]: [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF ---
commit ea739f7fbc71c6a534cb3103ddbdf3b31c5f391a
Author: Amery Hung <ameryhung@gmail.com>
Subject: bpf: Classify scalar kfunc arguments from BTF
This patch updates how scalar kfunc arguments are handled by moving their
classification out of the inline register state validation and into BTF-based
classification. It adds specific KF_ARG_* kinds for scalars and separates
BTF classification from register validation in check_kfunc_args.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 24858a38c34cf..ba2608592bd5f 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -12130,11 +12122,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> if (reg_is_referenced(env, reg))
> update_ref_obj(&meta->ref_obj, reg);
>
> - ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
> - ref_tname = btf_name_by_offset(btf, ref_t->name_off);
> + if (btf_type_is_ptr(t)) {
> + ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
> + ref_tname = btf_name_by_offset(btf, ref_t->name_off);
> + }
>
> - kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname,
> - args, i, nargs, argno);
> + kf_arg_type = get_kfunc_arg_type(env, meta, t, ref_t, ref_tname,
> + args, i, nargs, argno);
[Severity: Low]
If a kfunc call has a scalar argument, btf_type_is_ptr(t) evaluates to false,
skipping the initialization of ref_t and ref_tname.
Could this result in passing uninitialized variables from the stack by value
into get_kfunc_arg_type() during check_kfunc_args()?
[ ... ]
> @@ -12435,6 +12462,19 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> }
> }
> break;
> + case KF_ARG_CONST_MEM_SIZE:
> + if (reg->type != SCALAR_VALUE) {
> + verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
> + return -EINVAL;
> + }
> +
> + ret = process_const_arg(env, reg, argno, meta);
> + if (ret < 0)
> + return ret;
> +
> + fallthrough;
> + case KF_ARG_MEM_SIZE:
> + break;
[Severity: Critical]
Does this allow an eBPF program to bypass the scalar check by passing a
non-scalar register as the size parameter?
Because the KF_ARG_MEM_SIZE case label is placed after the SCALAR_VALUE
check inside KF_ARG_CONST_MEM_SIZE, an argument that is exactly
KF_ARG_MEM_SIZE will jump directly to its label and break out, leaving the
register's type unchecked.
Could this allow programs to pass a pointer as a buffer size, potentially
leading to out-of-bounds memory accesses?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723050806.1158442-1-ameryhung@gmail.com?part=17
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH bpf-next v1 18/18] bpf: Generate kfunc argument prototype at add-call time
2026-07-23 5:07 [PATCH bpf-next v1 00/18] Generate bpf_func_proto for kfunc Amery Hung
` (16 preceding siblings ...)
2026-07-23 5:08 ` [PATCH bpf-next v1 17/18] bpf: Classify scalar kfunc arguments from BTF Amery Hung
@ 2026-07-23 5:08 ` Amery Hung
17 siblings, 0 replies; 26+ messages in thread
From: Amery Hung @ 2026-07-23 5: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 | 127 +++++++++++++++++++++++++++--------
5 files changed, 138 insertions(+), 49 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 4fd360228321..2ea5ce30f729 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 47fe047ad30b..a30e7471e9d8 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 ba2608592bd5..aca37a7bf2cb 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;
@@ -8358,9 +8393,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];
@@ -8864,7 +8899,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)
@@ -8881,7 +8916,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)
@@ -8912,7 +8947,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)
@@ -8931,7 +8966,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)
@@ -8947,7 +8982,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)) {
@@ -10338,9 +10373,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;
}
@@ -11367,13 +11403,15 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
static int
get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- const struct btf_type *t, const struct btf_type *ref_t,
- const char *ref_tname, const struct btf_param *args,
- int arg, int nargs, argno_t argno)
+ const struct btf_param *args, int arg, int nargs, argno_t argno)
{
+ const struct btf_type *t, *ref_t = NULL;
+ 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]))
@@ -11394,6 +11432,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
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
@@ -11473,6 +11514,45 @@ 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++) {
+ argno_t argno = argno_from_arg(i + 1);
+
+ 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, argno);
+ 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 +12136,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 +12152,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,11 +12197,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, 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;
@@ -12694,7 +12759,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;
@@ -12993,6 +13058,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;
@@ -13009,6 +13075,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] 26+ messages in thread