* [PATCH bpf-next v2 1/7] bpf: Remove dynptr check in check_stack_range_initialized()
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
@ 2026-07-15 6:40 ` Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta Amery Hung
` (6 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Amery Hung @ 2026-07-15 6:40 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
For a MEM_UNINIT ("raw mode") helper argument,
check_stack_range_initialized() open-coded a scan that rejected any
STACK_DYNPTR slot in the range with "potential write to dynptr". This
duplicated, and was stricter than, the handling that runs when the
buffer is actually marked initialized. check_helper_call() later replays
the write byte by byte via check_mem_access(), which goes through
destroy_if_dynptr_stack_slot(), which rejects overwritting a referenced
dynptr. Therefore drop the redundant scan and rely on check_mem_access().
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 25 -------------------
.../testing/selftests/bpf/progs/dynptr_fail.c | 4 +--
2 files changed, 2 insertions(+), 27 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 03e2202cca13..7dd961ede88d 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6733,31 +6733,6 @@ static int check_stack_range_initialized(
}
if (meta && meta->raw_mode) {
- /* Ensure we won't be overwriting dynptrs when simulating byte
- * by byte access in check_helper_call using meta.access_size.
- * This would be a problem if we have a helper in the future
- * which takes:
- *
- * helper(uninit_mem, len, dynptr)
- *
- * Now, uninint_mem may overlap with dynptr pointer. Hence, it
- * may end up writing to dynptr itself when touching memory from
- * arg 1. This can be relaxed on a case by case basis for known
- * safe cases, but reject due to the possibilitiy of aliasing by
- * default.
- */
- for (i = min_off; i < max_off + access_size; i++) {
- int stack_off = -i - 1;
-
- spi = bpf_get_spi(i);
- /* raw_mode may write past allocated_stack */
- if (state->allocated_stack <= stack_off)
- continue;
- if (state->stack[spi].slot_type[stack_off % BPF_REG_SIZE] == STACK_DYNPTR) {
- verbose(env, "potential write to dynptr at off=%d disallowed\n", i);
- return -EACCES;
- }
- }
meta->access_size = access_size;
meta->regno = reg_from_argno(argno);
return 0;
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index 344fb2aa0813..94489ac64da8 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -1112,7 +1112,7 @@ int dynptr_overwrite_ref(void *ctx)
/* Reject writes to dynptr slot from bpf_dynptr_read */
SEC("?raw_tp")
-__failure __msg("potential write to dynptr at off=-16")
+__failure __msg("cannot overwrite referenced dynptr")
int dynptr_read_into_slot(void *ctx)
{
union {
@@ -1558,7 +1558,7 @@ int BPF_PROG(skb_invalid_ctx_fexit, void *skb)
/* Reject writes to dynptr slot for uninit arg */
SEC("?raw_tp")
-__failure __msg("potential write to dynptr at off=-16")
+__failure __msg("cannot overwrite referenced dynptr")
int uninit_write_into_slot(void *ctx)
{
struct {
--
2.52.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 1/7] bpf: Remove dynptr check in check_stack_range_initialized() Amery Hung
@ 2026-07-15 6:40 ` Amery Hung
2026-07-15 7:09 ` sashiko-bot
2026-07-15 7:48 ` bot+bpf-ci
2026-07-15 6:40 ` [PATCH bpf-next v2 3/7] bpf: Pass argno to callees in check_func_arg() instead of argno_from_reg(regno) Amery Hung
` (5 subsequent siblings)
7 siblings, 2 replies; 12+ messages in thread
From: Amery Hung @ 2026-07-15 6:40 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
To prepare for unifying the helper and kfunc call_arg_meta, group the
scattered MEM_UNINIT ("raw") memory argument fields (raw_mode, regno and
access_size) into a new struct arg_raw_mem_desc. The intention is to
make it clear about when these are set and used instead of fields with
overly generic names.
Identify the raw argument once, up front, in check_raw_mode_ok() (like
check_proto_release_reg() does for release_regno), recording its regno.
check_stack_range_initialized() now recognizes the raw buffer by matching
that regno, so the separate raw_mode flag is no longer needed, and the
per-argument "meta->raw_mode = arg_type & MEM_UNINIT" assignments in
check_func_arg() go away with it.
A raw memory argument can be tagged either ARG_PTR_TO_MEM | MEM_UNINIT or
ARG_PTR_TO_MAP_VALUE | MEM_UNINIT (the output buffer of bpf_map_pop_elem()
and bpf_map_peek_elem()). Either may be passed as a PTR_TO_STACK, which
reaches check_stack_range_initialized() through check_helper_mem_access(),
so both must be treated as raw. Extend arg_type_is_raw_mem() to match the
map value case as well; otherwise check_raw_mode_ok() would not record the
regno for it and an uninitialized stack buffer passed to those helpers
would be wrongly rejected for programs without CAP_PERFMON.
No functional change intended. This patch does not enable raw_mode
memory access for kfunc (i.e., uninit stack will not be allowed to be
passed to kfunc for unprivileged programs). Existing kfuncs with arguments
tagged with __uninit are either priviledged or dynptr kfuncs, which take
another path to make sure the access is checked by check_mem_access().
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf_verifier.h | 9 ++++++
kernel/bpf/verifier.c | 58 +++++++++++++++++-------------------
2 files changed, 36 insertions(+), 31 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 317e99b9acc0..d3592f5b8621 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1464,6 +1464,15 @@ struct ref_obj_desc {
u8 cnt;
};
+/*
+ * A memory argument a call fills in. The verifier allows the stack to be uninitialized if
+ * the range is a known constant. Stack slots are marked as STACK_MISC by check_mem_access().
+ */
+struct arg_raw_mem_desc {
+ u8 regno;
+ int size;
+};
+
struct bpf_kfunc_call_arg_meta {
/* In parameters */
struct btf *btf;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7dd961ede88d..1a41f99a9133 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -256,11 +256,9 @@ struct bpf_call_arg_meta {
struct bpf_map_desc map;
struct bpf_dynptr_desc dynptr;
struct ref_obj_desc ref_obj;
- bool raw_mode;
+ struct arg_raw_mem_desc arg_raw_mem;
bool pkt_access;
u8 release_regno;
- int regno;
- int access_size;
int mem_size;
u64 msize_max_value;
int func_id;
@@ -6690,6 +6688,8 @@ static int check_stack_range_initialized(
* but BTF based global subprog validation isn't accurate enough.
*/
bool allow_poison = access_size < 0 || clobber;
+ /* The call will initialize the memory; uninitialized stack allowed */
+ bool raw_mode = meta && meta->arg_raw_mem.regno == reg_from_argno(argno);
access_size = abs(access_size);
@@ -6725,16 +6725,14 @@ static int check_stack_range_initialized(
* helper return since specific bounds are unknown what may
* cause uninitialized stack leaking.
*/
- if (meta && meta->raw_mode)
- meta = NULL;
+ raw_mode = false;
min_off = reg_smin(reg) + off;
max_off = reg_smax(reg) + off;
}
- if (meta && meta->raw_mode) {
- meta->access_size = access_size;
- meta->regno = reg_from_argno(argno);
+ if (raw_mode) {
+ meta->arg_raw_mem.size = access_size;
return 0;
}
@@ -7727,7 +7725,13 @@ static bool arg_type_is_mem_size(enum bpf_arg_type type)
static bool arg_type_is_raw_mem(enum bpf_arg_type type)
{
- return base_type(type) == ARG_PTR_TO_MEM &&
+ /*
+ * A map value output buffer (e.g. bpf_map_pop_elem) is also a raw
+ * (uninitialized) memory argument, and like ARG_PTR_TO_MEM it may be
+ * passed as a PTR_TO_STACK that reaches check_stack_range_initialized().
+ */
+ return (base_type(type) == ARG_PTR_TO_MEM ||
+ base_type(type) == ARG_PTR_TO_MAP_VALUE) &&
type & MEM_UNINIT;
}
@@ -8403,7 +8407,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
verifier_bug(env, "invalid map_ptr to access map->value");
return -EFAULT;
}
- meta->raw_mode = arg_type & MEM_UNINIT;
err = check_helper_mem_access(env, reg, argno_from_reg(regno), meta->map.ptr->value_size,
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
false, meta);
@@ -8446,7 +8449,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
/* The access to this pointer is only checked when we hit the
* next is_mem_size argument below.
*/
- meta->raw_mode = arg_type & MEM_UNINIT;
if (arg_type & MEM_FIXED_SIZE) {
err = check_helper_mem_access(env, reg, argno_from_reg(regno), fn->arg_size[arg],
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
@@ -8797,26 +8799,19 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
return -EINVAL;
}
-static bool check_raw_mode_ok(const struct bpf_func_proto *fn)
+static bool check_raw_mode_ok(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
{
- int count = 0;
+ int i;
- if (arg_type_is_raw_mem(fn->arg1_type))
- count++;
- if (arg_type_is_raw_mem(fn->arg2_type))
- count++;
- if (arg_type_is_raw_mem(fn->arg3_type))
- count++;
- if (arg_type_is_raw_mem(fn->arg4_type))
- count++;
- if (arg_type_is_raw_mem(fn->arg5_type))
- count++;
+ for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
+ if (!arg_type_is_raw_mem(fn->arg_type[i]))
+ continue;
+ if (meta->arg_raw_mem.regno)
+ return false;
+ meta->arg_raw_mem.regno = i + 1;
+ }
- /* We only support one arg being in raw mode at the moment,
- * which is sufficient for the helper functions we have
- * right now.
- */
- return count <= 1;
+ return true;
}
static bool check_args_pair_invalid(const struct bpf_func_proto *fn, int arg)
@@ -8906,7 +8901,7 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
static int check_func_proto(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
{
- return check_raw_mode_ok(fn) &&
+ return check_raw_mode_ok(fn, meta) &&
check_arg_pair_ok(fn) &&
check_mem_arg_rw_flag_ok(fn) &&
check_proto_release_reg(fn, meta) &&
@@ -10303,8 +10298,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
/* Mark slots with STACK_MISC in case of raw mode, stack offset
* is inferred from register state.
*/
- for (i = 0; i < meta.access_size; i++) {
- err = check_mem_access(env, insn_idx, regs + meta.regno, argno_from_reg(meta.regno), i, BPF_B,
+ for (i = 0; i < meta.arg_raw_mem.size; i++) {
+ err = check_mem_access(env, insn_idx, regs + meta.arg_raw_mem.regno,
+ argno_from_reg(meta.arg_raw_mem.regno), i, BPF_B,
BPF_WRITE, -1, false, false);
if (err)
return err;
--
2.52.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta
2026-07-15 6:40 ` [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta Amery Hung
@ 2026-07-15 7:09 ` sashiko-bot
2026-07-15 7:48 ` bot+bpf-ci
1 sibling, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-15 7:09 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:
- [Critical] `bpf_map_peek_elem` reads uninitialized stack memory when used with `BPF_MAP_TYPE_BLOOM_FILTER`, causing a kernel stack information leak.
--
--- Patch [2]: [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta ---
commit b67be9cc9af835cbe1e71cd21cd41e24382f1240
Author: Amery Hung <ameryhung@gmail.com>
bpf: Factor out raw_mode-related fields in bpf_call_arg_meta
This patch groups scattered MEM_UNINIT memory argument fields into a new
struct arg_raw_mem_desc. It identifies raw arguments upfront in
check_raw_mode_ok(), matching them by register number in
check_stack_range_initialized() to eliminate the separate raw_mode flag.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 7dd961ede88df..1a41f99a9133c 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -7727,7 +7725,13 @@ static bool arg_type_is_mem_size(enum bpf_arg_type type)
>
> static bool arg_type_is_raw_mem(enum bpf_arg_type type)
> {
> - return base_type(type) == ARG_PTR_TO_MEM &&
> + /*
> + * A map value output buffer (e.g. bpf_map_pop_elem) is also a raw
> + * (uninitialized) memory argument, and like ARG_PTR_TO_MEM it may be
> + * passed as a PTR_TO_STACK that reaches check_stack_range_initialized().
> + */
> + return (base_type(type) == ARG_PTR_TO_MEM ||
> + base_type(type) == ARG_PTR_TO_MAP_VALUE) &&
> type & MEM_UNINIT;
> }
[Severity: Critical]
This is a pre-existing issue, but does treating ARG_PTR_TO_MAP_VALUE as
uninitialized memory expose a stack information leak when used with
bpf_map_peek_elem() and Bloom filters?
The verifier prototype bpf_map_peek_elem_proto specifies MEM_UNINIT | MEM_WRITE,
which assumes the map operation acts as an output buffer and will initialize
the memory.
However, when called on a BPF_MAP_TYPE_BLOOM_FILTER, bloom_map_peek_elem()
uses the buffer strictly as an input to compute a hash without writing to it:
kernel/bpf/bloom_filter.c:bloom_map_peek_elem() {
...
for (i = 0; i < bloom->nr_hash_funcs; i++) {
h = hash(bloom, value, map->value_size, i);
if (!test_bit(h, bloom->bitset))
return -ENOENT;
}
...
}
Since the map never initializes the buffer, but the verifier believes it
does, could a BPF program use this to read uninitialized kernel stack memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715064047.1793790-1-ameryhung@gmail.com?part=2
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta
2026-07-15 6:40 ` [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta Amery Hung
2026-07-15 7:09 ` sashiko-bot
@ 2026-07-15 7:48 ` bot+bpf-ci
1 sibling, 0 replies; 12+ messages in thread
From: bot+bpf-ci @ 2026-07-15 7:48 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2645 bytes --]
> commit 898896816b1b94bb310b2829155d616641a4e0b3
> Author: Amery Hung <ameryhung@gmail.com>
>
> bpf: Factor out raw_mode-related fields in bpf_call_arg_meta
>
> To prepare for unifying the helper and kfunc call_arg_meta, group the
> scattered MEM_UNINIT ("raw") memory argument fields (raw_mode, regno and
> access_size) into a new struct arg_raw_mem_desc. The intention is to
> make it clear about when these are set and used instead of fields with
> overly generic names.
>
> Identify the raw argument once, up front, in check_raw_mode_ok() (like
> check_proto_release_reg() does for release_regno), recording its regno.
> check_stack_range_initialized() now recognizes the raw buffer by matching
> that regno, so the separate raw_mode flag is no longer needed, and the
> per-argument "meta->raw_mode = arg_type & MEM_UNINIT" assignments in
> check_func_arg() go away with it.
>
> A raw memory argument can be tagged either ARG_PTR_TO_MEM | MEM_UNINIT or
> ARG_PTR_TO_MAP_VALUE | MEM_UNINIT (the output buffer of bpf_map_pop_elem()
> and bpf_map_peek_elem()). Either may be passed as a PTR_TO_STACK, which
> reaches check_stack_range_initialized() through check_helper_mem_access(),
> so both must be treated as raw. Extend arg_type_is_raw_mem() to match the
> map value case as well; otherwise check_raw_mode_ok() would not record the
> regno for it and an uninitialized stack buffer passed to those helpers
> would be wrongly rejected for programs without CAP_PERFMON.
>
> No functional change intended. This patch does not enable raw_mode
> memory access for kfunc (i.e., uninit stack will not be allowed to be
> passed to kfunc for unprivileged programs). Existing kfuncs with arguments
> tagged with __uninit are either priviledged or dynptr kfuncs, which take
> another path to make sure the access is checked by check_mem_access().
>
> Acked-by: Eduard Zingerman <eddyz87@gmail.com>
> Signed-off-by: Amery Hung <ameryhung@gmail.com>
This looks like a bug fix for MEM_UNINIT handling with map value
arguments. The commit message explains that without the arg_type_is_raw_mem()
extension to match ARG_PTR_TO_MAP_VALUE | MEM_UNINIT, valid programs would be
wrongly rejected. Should this include:
Fixes: 18752d73c1898 ("bpf: Improve check_raw_mode_ok test for MEM_UNINIT-tagged types")
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29395575390
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH bpf-next v2 3/7] bpf: Pass argno to callees in check_func_arg() instead of argno_from_reg(regno)
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 1/7] bpf: Remove dynptr check in check_stack_range_initialized() Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta Amery Hung
@ 2026-07-15 6:40 ` Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 4/7] bpf: Unify helper and kfunc allocation-size argument handling Amery Hung
` (4 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Amery Hung @ 2026-07-15 6:40 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_func_arg() only ever handles register arguments (the caller
loops over the first MAX_BPF_FUNC_REG_ARGS arguments), so a single
argno_t built from the register number identifies the argument for
every callee. Remove the duplicated argno_from_reg() calls to
simplify check_func_arg().
'regno' is still kept for the few places that need the raw register
number directly (register reads, verbose R%d messages) and for
referring to the neighbouring size/memory argument in the
ARG_CONST_SIZE{,_OR_ZERO} cases.
No functional change intended.
Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 36 +++++++++++++++++-------------------
1 file changed, 17 insertions(+), 19 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1a41f99a9133..529ad0b4fcbd 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8275,7 +8275,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
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];
- argno_t argno = argno_from_arg(arg + 1);
+ argno_t argno = argno_from_reg(regno);
enum bpf_reg_type type = reg->type;
u32 *arg_btf_id = NULL;
u32 key_size;
@@ -8320,11 +8320,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK)
arg_btf_id = fn->arg_btf_id[arg];
- err = check_reg_type(env, reg, argno_from_reg(regno), arg_type, arg_btf_id, meta);
+ err = check_reg_type(env, reg, argno, arg_type, arg_btf_id, meta);
if (err)
return err;
- err = check_func_arg_reg_off(env, reg, argno_from_reg(regno), arg_type);
+ err = check_func_arg_reg_off(env, reg, argno, arg_type);
if (err)
return err;
@@ -8381,7 +8381,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return -EFAULT;
}
key_size = meta->map.ptr->key_size;
- err = check_helper_mem_access(env, reg, argno_from_reg(regno), key_size, BPF_READ, false, NULL);
+ err = check_helper_mem_access(env, reg, argno, key_size, BPF_READ, false, NULL);
if (err)
return err;
if (can_elide_value_nullness(meta->map.ptr)) {
@@ -8407,7 +8407,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
verifier_bug(env, "invalid map_ptr to access map->value");
return -EFAULT;
}
- err = check_helper_mem_access(env, reg, argno_from_reg(regno), meta->map.ptr->value_size,
+ err = check_helper_mem_access(env, reg, argno, meta->map.ptr->value_size,
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
false, meta);
break;
@@ -8425,11 +8425,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return -EACCES;
}
if (meta->func_id == BPF_FUNC_spin_lock) {
- err = process_spin_lock(env, reg, argno_from_reg(regno), PROCESS_SPIN_LOCK);
+ err = process_spin_lock(env, reg, argno, PROCESS_SPIN_LOCK);
if (err)
return err;
} else if (meta->func_id == BPF_FUNC_spin_unlock) {
- err = process_spin_lock(env, reg, argno_from_reg(regno), 0);
+ err = process_spin_lock(env, reg, argno, 0);
if (err)
return err;
} else {
@@ -8438,7 +8438,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_from_reg(regno), meta);
+ err = process_timer_helper(env, reg, argno, meta);
if (err)
return err;
break;
@@ -8450,7 +8450,7 @@ 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_from_reg(regno), fn->arg_size[arg],
+ err = check_helper_mem_access(env, reg, argno, fn->arg_size[arg],
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
false, meta);
if (err)
@@ -8460,21 +8460,19 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
}
break;
case ARG_CONST_SIZE:
- err = check_mem_size_reg(env, reg_state(env, regno - 1), reg, argno_from_reg(regno - 1),
- argno_from_reg(regno),
- fn->arg_type[arg - 1] & MEM_WRITE ?
- BPF_WRITE : BPF_READ,
+ 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_from_reg(regno),
- fn->arg_type[arg - 1] & MEM_WRITE ?
- BPF_WRITE : BPF_READ,
+ 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);
break;
case ARG_PTR_TO_DYNPTR:
- err = process_dynptr_func(env, reg, argno_from_reg(regno), insn_idx, arg_type, &meta->ref_obj,
+ err = process_dynptr_func(env, reg, argno, insn_idx, arg_type, &meta->ref_obj,
&meta->dynptr);
if (err)
return err;
@@ -8492,7 +8490,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
break;
case ARG_PTR_TO_CONST_STR:
{
- err = check_arg_const_str(env, reg, argno_from_reg(regno));
+ err = check_arg_const_str(env, reg, argno);
if (err)
return err;
break;
--
2.52.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH bpf-next v2 4/7] bpf: Unify helper and kfunc allocation-size argument handling
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
` (2 preceding siblings ...)
2026-07-15 6:40 ` [PATCH bpf-next v2 3/7] bpf: Pass argno to callees in check_func_arg() instead of argno_from_reg(regno) Amery Hung
@ 2026-07-15 6:40 ` Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 5/7] selftests/bpf: Test kfunc returning zero-sized allocation buffer Amery Hung
` (3 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Amery Hung @ 2026-07-15 6:40 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
The constant "size of the PTR_TO_MEM returned in R0" argument is handled by
both helpers (ARG_CONST_ALLOC_SIZE_OR_ZERO) and kfuncs (__rdonly_buf_size /
__rdwr_buf_size), each with its own meta field (meta->mem_size,
meta->r0_size) and duplicated validation. Add struct arg_alloc_mem_desc
and a shared process_const_alloc_mem_size(), and replace both fields with
meta->arg_alloc_mem.
The desc records presence with a 'found' flag instead of using a non-zero
size as the sentinel. This also fixes a pre-existing bug on the kfunc
return path: "no size argument" was tested as r0_size == 0, so an explicit
__rdonly_buf_size/__rdwr_buf_size of 0 was treated as absent and fell
through to btf_resolve_size(), giving R0 the size of the pointed-to return
type instead of 0. With 'found', an explicit zero size is honored and
btf_resolve_size() is used only when no size argument was passed.
The size is stored in a u32, matching regs[R0].mem_size. The U32_MAX
check now apply to both helper and kfunc through
process_const_alloc_mem_size().
Fold bpf_session_cookie return size assignment into current kfunc return
size resolution path.
Note that verifier saves kfunc return size through r0_size instead of
mem_size. The later has no active readers so remove it.
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf_verifier.h | 9 +-
kernel/bpf/verifier.c | 87 ++++++++++---------
.../selftests/bpf/prog_tests/kfunc_call.c | 2 +-
3 files changed, 55 insertions(+), 43 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index d3592f5b8621..e3eda72cbf67 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1473,6 +1473,12 @@ struct arg_raw_mem_desc {
int size;
};
+/* Size of PTR_TO_MEM returned, taken from a constant allocation-size argument */
+struct ret_mem_desc {
+ u32 size;
+ bool found;
+};
+
struct bpf_kfunc_call_arg_meta {
/* In parameters */
struct btf *btf;
@@ -1484,7 +1490,6 @@ struct bpf_kfunc_call_arg_meta {
u8 release_regno;
bool r0_rdonly;
u32 ret_btf_id;
- u64 r0_size;
u32 subprogno;
struct {
u64 value;
@@ -1519,7 +1524,7 @@ struct bpf_kfunc_call_arg_meta {
struct bpf_map_desc map;
struct bpf_dynptr_desc dynptr;
struct ref_obj_desc ref_obj;
- u64 mem_size;
+ struct ret_mem_desc ret_mem;
};
int bpf_get_helper_proto(struct bpf_verifier_env *env, int func_id,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 529ad0b4fcbd..463b49df4ff9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -257,9 +257,9 @@ struct bpf_call_arg_meta {
struct bpf_dynptr_desc dynptr;
struct ref_obj_desc ref_obj;
struct arg_raw_mem_desc arg_raw_mem;
+ struct ret_mem_desc ret_mem;
bool pkt_access;
u8 release_regno;
- int mem_size;
u64 msize_max_value;
int func_id;
struct btf *btf;
@@ -6974,6 +6974,40 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
return err;
}
+static int process_const_alloc_mem_size(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, struct ret_mem_desc *ret_mem)
+{
+ int regno = reg_from_argno(argno);
+ int err;
+
+ if (ret_mem->found) {
+ verifier_bug(env, "only one allocation size argument permitted");
+ return -EFAULT;
+ }
+
+ if (!tnum_is_const(reg->var_off)) {
+ verbose(env, "%s is not a const\n", reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ if (reg->var_off.value > U32_MAX) {
+ verbose(env, "%s allocation size exceeds u32 max\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)
+ return err;
+
+ ret_mem->size = reg->var_off.value;
+ ret_mem->found = true;
+
+ 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)
{
@@ -8478,13 +8512,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
break;
case ARG_CONST_ALLOC_SIZE_OR_ZERO:
- if (!tnum_is_const(reg->var_off)) {
- verbose(env, "R%d is not a known constant'\n",
- regno);
- return -EACCES;
- }
- meta->mem_size = reg->var_off.value;
- err = mark_chain_precision(env, regno);
+ err = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
if (err)
return err;
break;
@@ -10516,7 +10544,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
case RET_PTR_TO_MEM:
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag;
- regs[BPF_REG_0].mem_size = meta.mem_size;
+ regs[BPF_REG_0].mem_size = meta.ret_mem.size;
break;
case RET_PTR_TO_MEM_OR_BTF_ID:
{
@@ -12066,28 +12094,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
}
if (is_ret_buf_sz) {
- if (meta->r0_size) {
- verbose(env, "2 or more rdonly/rdwr_buf_size parameters for kfunc");
- return -EINVAL;
- }
-
- if (!tnum_is_const(reg->var_off)) {
- verbose(env, "%s is not a const\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
-
- meta->r0_size = reg->var_off.value;
- if (meta->r0_size > U32_MAX) {
- verbose(env, "%s rdonly/rdwr_buf_size exceeds u32 max\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 (ret)
+ ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
+ if (ret < 0)
return ret;
}
continue;
@@ -13066,11 +13074,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
}
- if (meta.func_id == special_kfunc_list[KF_bpf_session_cookie]) {
- meta.r0_size = sizeof(u64);
- meta.r0_rdonly = false;
- }
-
if (is_bpf_wq_set_callback_kfunc(meta.func_id)) {
err = push_callback_call(env, insn, insn_idx, meta.subprogno,
set_timer_callback_state);
@@ -13203,15 +13206,19 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
/* kfunc returning 'void *' is equivalent to returning scalar */
mark_reg_unknown(env, regs, BPF_REG_0);
} else if (!__btf_type_is_struct(ptr_type)) {
- if (!meta.r0_size) {
+ if (!meta.ret_mem.found) {
__u32 sz;
if (!IS_ERR(btf_resolve_size(desc_btf, ptr_type, &sz))) {
- meta.r0_size = sz;
+ meta.ret_mem.found = true;
+ meta.ret_mem.size = sz;
meta.r0_rdonly = true;
}
+
+ if (meta.func_id == special_kfunc_list[KF_bpf_session_cookie])
+ meta.r0_rdonly = false;
}
- if (!meta.r0_size) {
+ if (!meta.ret_mem.found) {
ptr_type_name = btf_name_by_offset(desc_btf,
ptr_type->name_off);
verbose(env,
@@ -13224,7 +13231,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_MEM;
- regs[BPF_REG_0].mem_size = meta.r0_size;
+ regs[BPF_REG_0].mem_size = meta.ret_mem.size;
if (meta.r0_rdonly)
regs[BPF_REG_0].type |= MEM_RDONLY;
diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
index 67a30bf69509..c9fce95d220e 100644
--- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
@@ -66,7 +66,7 @@ static struct kfunc_test_params kfunc_tests[] = {
TC_FAIL(kfunc_call_test_get_mem_fail_rdonly, 0, "R0 cannot write into rdonly_mem"),
TC_FAIL(kfunc_call_test_get_mem_fail_use_after_free, 0, "invalid mem access 'scalar'"),
TC_FAIL(kfunc_call_test_get_mem_fail_oob, 0, "min value is outside of the allowed memory range"),
- TC_FAIL(kfunc_call_test_get_mem_fail_oversized, 0, "rdonly/rdwr_buf_size exceeds u32 max"),
+ TC_FAIL(kfunc_call_test_get_mem_fail_oversized, 0, "allocation size exceeds u32 max"),
TC_FAIL(kfunc_call_test_get_mem_fail_not_const, 0, "is not a const"),
TC_FAIL(kfunc_call_test_mem_acquire_fail, 0, "acquire kernel function does not return PTR_TO_BTF_ID"),
TC_FAIL(kfunc_call_test_pointer_arg_type_mismatch, 0, "R1 expected pointer to ctx, but got scalar"),
--
2.52.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH bpf-next v2 5/7] selftests/bpf: Test kfunc returning zero-sized allocation buffer
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
` (3 preceding siblings ...)
2026-07-15 6:40 ` [PATCH bpf-next v2 4/7] bpf: Unify helper and kfunc allocation-size argument handling Amery Hung
@ 2026-07-15 6:40 ` Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 6/7] bpf: Drop redundant pkt_access from bpf_call_arg_meta Amery Hung
` (2 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Amery Hung @ 2026-07-15 6:40 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Add a test passing an explicit rdwr_buf_size of 0 to
bpf_kfunc_call_test_get_rdwr_mem() and then reading the returned R0.
R0 should be a zero-sized PTR_TO_MEM, so the access must be rejected
with "min value is outside of the allowed memory range".
This covers the pre-existing bug where a zero size argument was
treated as "no size argument": the verifier fell through to
btf_resolve_size() and sized R0 after the pointed-to return type,
wrongly allowing the read.
Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
.../selftests/bpf/prog_tests/kfunc_call.c | 1 +
.../selftests/bpf/progs/kfunc_call_fail.c | 27 +++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
index c9fce95d220e..7af5560f2a08 100644
--- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
@@ -66,6 +66,7 @@ static struct kfunc_test_params kfunc_tests[] = {
TC_FAIL(kfunc_call_test_get_mem_fail_rdonly, 0, "R0 cannot write into rdonly_mem"),
TC_FAIL(kfunc_call_test_get_mem_fail_use_after_free, 0, "invalid mem access 'scalar'"),
TC_FAIL(kfunc_call_test_get_mem_fail_oob, 0, "min value is outside of the allowed memory range"),
+ TC_FAIL(kfunc_call_test_get_mem_fail_zero_size, 0, "min value is outside of the allowed memory range"),
TC_FAIL(kfunc_call_test_get_mem_fail_oversized, 0, "allocation size exceeds u32 max"),
TC_FAIL(kfunc_call_test_get_mem_fail_not_const, 0, "is not a const"),
TC_FAIL(kfunc_call_test_mem_acquire_fail, 0, "acquire kernel function does not return PTR_TO_BTF_ID"),
diff --git a/tools/testing/selftests/bpf/progs/kfunc_call_fail.c b/tools/testing/selftests/bpf/progs/kfunc_call_fail.c
index 6144ce3ff0b2..64b6a0b0ab1c 100644
--- a/tools/testing/selftests/bpf/progs/kfunc_call_fail.c
+++ b/tools/testing/selftests/bpf/progs/kfunc_call_fail.c
@@ -103,6 +103,33 @@ int kfunc_call_test_get_mem_fail_oob(struct __sk_buff *skb)
return ret;
}
+SEC("?tc")
+int kfunc_call_test_get_mem_fail_zero_size(struct __sk_buff *skb)
+{
+ struct prog_test_ref_kfunc *pt;
+ unsigned long s = 0;
+ int *p = NULL;
+ int ret = 0;
+
+ pt = bpf_kfunc_call_test_acquire(&s);
+ if (pt) {
+ /*
+ * An explicit rdwr_buf_size of 0 gives R0 a zero-sized buffer,
+ * so any access is out of bounds, hence -EACCES. Previously the
+ * verifier treated a zero size as "no size argument" and sized
+ * R0 after the pointed-to return type, wrongly allowing the read.
+ */
+ p = bpf_kfunc_call_test_get_rdwr_mem(pt, 0);
+ if (p)
+ ret = p[0];
+ else
+ ret = -1;
+
+ bpf_kfunc_call_test_release(pt);
+ }
+ return ret;
+}
+
SEC("?tc")
int kfunc_call_test_get_mem_fail_oversized(struct __sk_buff *skb)
{
--
2.52.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH bpf-next v2 6/7] bpf: Drop redundant pkt_access from bpf_call_arg_meta
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
` (4 preceding siblings ...)
2026-07-15 6:40 ` [PATCH bpf-next v2 5/7] selftests/bpf: Test kfunc returning zero-sized allocation buffer Amery Hung
@ 2026-07-15 6:40 ` Amery Hung
2026-07-15 6:40 ` [PATCH bpf-next v2 7/7] bpf: Unify helper and kfunc call argument meta Amery Hung
2026-07-15 9:10 ` [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta patchwork-bot+netdevbpf
7 siblings, 0 replies; 12+ messages in thread
From: Amery Hung @ 2026-07-15 6:40 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
meta->pkt_access is only ever a copy of fn->pkt_access, assigned once in
check_helper_call() and read back in may_access_direct_pkt_data(). Have
may_access_direct_pkt_data() take the bpf_func_proto and read
fn->pkt_access directly, and drop the meta field along with its
assignment.
The only non-NULL caller, check_func_arg(), already has fn in scope; the
remaining callers pass NULL and are unaffected.
No functional change intended.
Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 463b49df4ff9..37127ae1ff28 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -258,7 +258,6 @@ struct bpf_call_arg_meta {
struct ref_obj_desc ref_obj;
struct arg_raw_mem_desc arg_raw_mem;
struct ret_mem_desc ret_mem;
- bool pkt_access;
u8 release_regno;
u64 msize_max_value;
int func_id;
@@ -4673,7 +4672,7 @@ static int check_map_access(struct bpf_verifier_env *env, struct bpf_reg_state *
}
static bool may_access_direct_pkt_data(struct bpf_verifier_env *env,
- const struct bpf_call_arg_meta *meta,
+ const struct bpf_func_proto *fn,
enum bpf_access_type t)
{
enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
@@ -4697,8 +4696,8 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env,
case BPF_PROG_TYPE_LWT_XMIT:
case BPF_PROG_TYPE_SK_SKB:
case BPF_PROG_TYPE_SK_MSG:
- if (meta)
- return meta->pkt_access;
+ if (fn)
+ return fn->pkt_access;
env->seen_direct_write = true;
return true;
@@ -8332,7 +8331,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
}
if (type_is_pkt_pointer(type) &&
- !may_access_direct_pkt_data(env, meta, BPF_READ)) {
+ !may_access_direct_pkt_data(env, fn, BPF_READ)) {
verbose(env, "helper access to the packet is not allowed\n");
return -EACCES;
}
@@ -10285,7 +10284,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
}
memset(&meta, 0, sizeof(meta));
- meta.pkt_access = fn->pkt_access;
err = check_func_proto(fn, &meta);
if (err) {
--
2.52.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH bpf-next v2 7/7] bpf: Unify helper and kfunc call argument meta
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
` (5 preceding siblings ...)
2026-07-15 6:40 ` [PATCH bpf-next v2 6/7] bpf: Drop redundant pkt_access from bpf_call_arg_meta Amery Hung
@ 2026-07-15 6:40 ` Amery Hung
2026-07-15 7:00 ` sashiko-bot
2026-07-15 9:10 ` [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta patchwork-bot+netdevbpf
7 siblings, 1 reply; 12+ messages in thread
From: Amery Hung @ 2026-07-15 6:40 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Helper and kfunc argument checking carried two separate meta structs: the
verifier-local struct bpf_call_arg_meta and bpf_kfunc_call_arg_meta.
Merge them into a single struct bpf_call_arg_meta. This is groundwork for
sharing argument checking between helpers and kfuncs.
While merging, drop the btf_id field from the helper meta since it is
never used.
No functional change.
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf_verifier.h | 38 +++++++++------
kernel/bpf/cfg.c | 2 +-
kernel/bpf/verifier.c | 94 +++++++++++++++---------------------
3 files changed, 62 insertions(+), 72 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index e3eda72cbf67..682c2cd3b844 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1479,18 +1479,23 @@ struct ret_mem_desc {
bool found;
};
-struct bpf_kfunc_call_arg_meta {
- /* In parameters */
+struct bpf_call_arg_meta {
+ /* Common */
struct btf *btf;
u32 func_id;
- u32 kfunc_flags;
- const struct btf_type *func_proto;
- const char *func_name;
- /* Out parameters */
u8 release_regno;
- bool r0_rdonly;
u32 ret_btf_id;
u32 subprogno;
+ struct bpf_map_desc map;
+ struct bpf_dynptr_desc dynptr;
+ struct ref_obj_desc ref_obj;
+ struct ret_mem_desc ret_mem;
+
+ /* Only set by kfunc */
+ bool r0_rdonly;
+ u32 kfunc_flags;
+ const struct btf_type *func_proto;
+ const char *func_name;
struct {
u64 value;
bool found;
@@ -1521,28 +1526,31 @@ struct bpf_kfunc_call_arg_meta {
u8 spi;
u8 frameno;
} iter;
- struct bpf_map_desc map;
- struct bpf_dynptr_desc dynptr;
- struct ref_obj_desc ref_obj;
- struct ret_mem_desc ret_mem;
+
+ /* Only set by helper */
+ u64 msize_max_value;
+ s64 const_map_key;
+ struct btf *ret_btf;
+ struct btf_field *kptr_field;
+ struct arg_raw_mem_desc arg_raw_mem;
};
int bpf_get_helper_proto(struct bpf_verifier_env *env, int func_id,
const struct bpf_func_proto **ptr);
int bpf_fetch_kfunc_arg_meta(struct bpf_verifier_env *env, s32 func_id,
- s16 offset, struct bpf_kfunc_call_arg_meta *meta);
+ s16 offset, struct bpf_call_arg_meta *meta);
bool bpf_is_async_callback_calling_insn(struct bpf_insn *insn);
bool bpf_is_sync_callback_calling_insn(struct bpf_insn *insn);
-static inline bool bpf_is_iter_next_kfunc(struct bpf_kfunc_call_arg_meta *meta)
+static inline bool bpf_is_iter_next_kfunc(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_ITER_NEXT;
}
-static inline bool bpf_is_kfunc_sleepable(struct bpf_kfunc_call_arg_meta *meta)
+static inline bool bpf_is_kfunc_sleepable(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_SLEEPABLE;
}
-bool bpf_is_kfunc_pkt_changing(struct bpf_kfunc_call_arg_meta *meta);
+bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta);
struct bpf_iarray *bpf_iarray_realloc(struct bpf_iarray *old, size_t n_elem);
int bpf_copy_insn_array_uniq(struct bpf_map *map, u32 start, u32 end, u32 *off);
bool bpf_insn_is_cond_jump(u8 code);
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 26d37066465f..db3416a7c904 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -491,7 +491,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
return ret;
}
} else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
- struct bpf_kfunc_call_arg_meta meta;
+ struct bpf_call_arg_meta meta;
ret = bpf_fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta);
if (ret == 0 && bpf_is_iter_next_kfunc(&meta)) {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 37127ae1ff28..de816063ae63 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -252,24 +252,6 @@ static int validate_ref_obj(struct bpf_verifier_env *env, struct ref_obj_desc *r
return 0;
}
-struct bpf_call_arg_meta {
- struct bpf_map_desc map;
- struct bpf_dynptr_desc dynptr;
- struct ref_obj_desc ref_obj;
- struct arg_raw_mem_desc arg_raw_mem;
- struct ret_mem_desc ret_mem;
- u8 release_regno;
- u64 msize_max_value;
- int func_id;
- struct btf *btf;
- u32 btf_id;
- struct btf *ret_btf;
- u32 ret_btf_id;
- u32 subprogno;
- struct btf_field *kptr_field;
- s64 const_map_key;
-};
-
struct bpf_kfunc_meta {
struct btf *btf;
const struct btf_type *proto;
@@ -927,10 +909,10 @@ static void __mark_reg_known_zero(struct bpf_reg_state *reg);
static bool in_rcu_cs(struct bpf_verifier_env *env);
-static bool is_kfunc_rcu_protected(struct bpf_kfunc_call_arg_meta *meta);
+static bool is_kfunc_rcu_protected(struct bpf_call_arg_meta *meta);
static int mark_stack_slots_iter(struct bpf_verifier_env *env,
- struct bpf_kfunc_call_arg_meta *meta,
+ struct bpf_call_arg_meta *meta,
struct bpf_reg_state *reg, int insn_idx,
struct btf *btf, u32 btf_id, int nr_slots)
{
@@ -1063,7 +1045,7 @@ static int acquire_irq_state(struct bpf_verifier_env *env, int insn_idx);
static int release_irq_state(struct bpf_verifier_state *state, int id);
static int mark_stack_slot_irq_flag(struct bpf_verifier_env *env,
- struct bpf_kfunc_call_arg_meta *meta,
+ struct bpf_call_arg_meta *meta,
struct bpf_reg_state *reg, int insn_idx,
int kfunc_class)
{
@@ -7245,7 +7227,7 @@ static int process_timer_helper(struct bpf_verifier_env *env, struct bpf_reg_sta
}
static int process_timer_kfunc(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
return process_timer_func(env, reg, argno, &meta->map);
}
@@ -7412,23 +7394,23 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
return err;
}
-static bool is_iter_kfunc(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_iter_kfunc(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & (KF_ITER_NEW | KF_ITER_NEXT | KF_ITER_DESTROY);
}
-static bool is_iter_new_kfunc(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_iter_new_kfunc(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_ITER_NEW;
}
-static bool is_iter_destroy_kfunc(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_iter_destroy_kfunc(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_ITER_DESTROY;
}
-static bool is_kfunc_arg_iter(struct bpf_kfunc_call_arg_meta *meta, int arg_idx,
+static bool is_kfunc_arg_iter(struct bpf_call_arg_meta *meta, int arg_idx,
const struct btf_param *arg)
{
/* btf_check_iter_kfuncs() guarantees that first argument of any iter
@@ -7442,7 +7424,7 @@ static bool is_kfunc_arg_iter(struct bpf_kfunc_call_arg_meta *meta, int arg_idx,
}
static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno, int insn_idx,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
struct bpf_func_state *state = bpf_func(env, reg);
const struct btf_type *t;
@@ -7609,7 +7591,7 @@ static int widen_imprecise_scalars(struct bpf_verifier_env *env,
}
static struct bpf_reg_state *get_iter_from_state(struct bpf_verifier_state *cur_st,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
int iter_frameno = meta->iter.frameno;
int iter_spi = meta->iter.spi;
@@ -7696,7 +7678,7 @@ static struct bpf_reg_state *get_iter_from_state(struct bpf_verifier_state *cur_
* bpf_iter_num_destroy(&it);
*/
static int process_iter_next_call(struct bpf_verifier_env *env, int insn_idx,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
struct bpf_verifier_state *cur_st = env->cur_state, *queued_st, *prev_st;
struct bpf_func_state *cur_fr = cur_st->frame[cur_st->curframe], *queued_fr;
@@ -10750,27 +10732,27 @@ static void mark_btf_func_reg_size(struct bpf_verifier_env *env, u32 regno,
return __mark_btf_func_reg_size(env, cur_regs(env), regno, reg_size);
}
-static bool is_kfunc_acquire(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_acquire(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_ACQUIRE;
}
-static bool is_kfunc_release(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_release(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_RELEASE;
}
-static bool is_kfunc_destructive(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_destructive(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_DESTRUCTIVE;
}
-static bool is_kfunc_rcu(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_rcu(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_RCU;
}
-static bool is_kfunc_rcu_protected(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_rcu_protected(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_RCU_PROTECTED;
}
@@ -10991,7 +10973,7 @@ static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param
* To determine whether an argument is implicit, we compare its position
* against the number of arguments in the prototype w/o implicit args.
*/
-static bool is_kfunc_arg_implicit(const struct bpf_kfunc_call_arg_meta *meta, u32 arg_idx)
+static bool is_kfunc_arg_implicit(const struct bpf_call_arg_meta *meta, u32 arg_idx)
{
const struct btf_type *func, *func_proto;
u32 argn;
@@ -11290,7 +11272,7 @@ static bool is_task_work_add_kfunc(u32 func_id)
func_id == special_kfunc_list[KF_bpf_task_work_schedule_resume];
}
-static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_ret_null(struct bpf_call_arg_meta *meta)
{
if (is_bpf_refcount_acquire_kfunc(meta->func_id) && meta->arg_owning_ref)
return false;
@@ -11298,34 +11280,34 @@ static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta)
return meta->kfunc_flags & KF_RET_NULL;
}
-static bool is_kfunc_bpf_rcu_read_lock(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_bpf_rcu_read_lock(struct bpf_call_arg_meta *meta)
{
return meta->func_id == special_kfunc_list[KF_bpf_rcu_read_lock];
}
-static bool is_kfunc_bpf_rcu_read_unlock(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_bpf_rcu_read_unlock(struct bpf_call_arg_meta *meta)
{
return meta->func_id == special_kfunc_list[KF_bpf_rcu_read_unlock];
}
-static bool is_kfunc_bpf_preempt_disable(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_bpf_preempt_disable(struct bpf_call_arg_meta *meta)
{
return meta->func_id == special_kfunc_list[KF_bpf_preempt_disable];
}
-static bool is_kfunc_bpf_preempt_enable(struct bpf_kfunc_call_arg_meta *meta)
+static bool is_kfunc_bpf_preempt_enable(struct bpf_call_arg_meta *meta)
{
return meta->func_id == special_kfunc_list[KF_bpf_preempt_enable];
}
-bool bpf_is_kfunc_pkt_changing(struct bpf_kfunc_call_arg_meta *meta)
+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
get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_func_state *caller,
- struct bpf_reg_state *regs, struct bpf_kfunc_call_arg_meta *meta,
+ 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,
int arg, int nargs, argno_t argno, struct bpf_reg_state *reg)
@@ -11431,7 +11413,7 @@ 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,
const char *ref_tname, u32 ref_id,
- struct bpf_kfunc_call_arg_meta *meta,
+ struct bpf_call_arg_meta *meta,
int arg, argno_t argno)
{
const struct btf_type *reg_ref_t;
@@ -11501,7 +11483,7 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
}
static int process_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
int err, spi, kfunc_class = IRQ_NATIVE_KFUNC;
bool irq_save;
@@ -11826,7 +11808,7 @@ static bool check_kfunc_is_graph_node_api(struct bpf_verifier_env *env,
static int
__process_kf_arg_ptr_to_graph_root(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta,
+ struct bpf_call_arg_meta *meta,
enum btf_field_type head_field_type,
struct btf_field **head_field)
{
@@ -11876,7 +11858,7 @@ __process_kf_arg_ptr_to_graph_root(struct bpf_verifier_env *env,
static int process_kf_arg_ptr_to_list_head(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
return __process_kf_arg_ptr_to_graph_root(env, reg, argno, meta, BPF_LIST_HEAD,
&meta->arg_list_head.field);
@@ -11884,7 +11866,7 @@ static int process_kf_arg_ptr_to_list_head(struct bpf_verifier_env *env,
static int process_kf_arg_ptr_to_rbtree_root(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
return __process_kf_arg_ptr_to_graph_root(env, reg, argno, meta, BPF_RB_ROOT,
&meta->arg_rbtree_root.field);
@@ -11893,7 +11875,7 @@ static int process_kf_arg_ptr_to_rbtree_root(struct bpf_verifier_env *env,
static int
__process_kf_arg_ptr_to_graph_node(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta,
+ struct bpf_call_arg_meta *meta,
enum btf_field_type head_field_type,
enum btf_field_type node_field_type,
struct btf_field **node_field)
@@ -11958,7 +11940,7 @@ __process_kf_arg_ptr_to_graph_node(struct bpf_verifier_env *env,
static int process_kf_arg_ptr_to_list_node(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
return __process_kf_arg_ptr_to_graph_node(env, reg, argno, meta,
BPF_LIST_HEAD, BPF_LIST_NODE,
@@ -11967,7 +11949,7 @@ static int process_kf_arg_ptr_to_list_node(struct bpf_verifier_env *env,
static int process_kf_arg_ptr_to_rbtree_node(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, argno_t argno,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
return __process_kf_arg_ptr_to_graph_node(env, reg, argno, meta,
BPF_RB_ROOT, BPF_RB_NODE,
@@ -11996,7 +11978,7 @@ static bool check_css_task_iter_allowlist(struct bpf_verifier_env *env)
}
}
-static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta,
+static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
int insn_idx)
{
const char *func_name = meta->func_name, *ref_tname;
@@ -12575,7 +12557,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
int bpf_fetch_kfunc_arg_meta(struct bpf_verifier_env *env,
s32 func_id,
s16 offset,
- struct bpf_kfunc_call_arg_meta *meta)
+ struct bpf_call_arg_meta *meta)
{
struct bpf_kfunc_meta kfunc;
int err;
@@ -12734,7 +12716,7 @@ s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *
int arg, int insn_idx)
{
struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
- struct bpf_kfunc_call_arg_meta meta;
+ struct bpf_call_arg_meta meta;
const struct btf_param *args;
const struct btf_type *t, *ref_t;
const struct btf *btf;
@@ -12795,7 +12777,7 @@ s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *
* 0 - fall-through to 'else' branch
* < 0 - not fall-through to 'else' branch, return error
*/
-static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta,
+static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
struct bpf_reg_state *regs, struct bpf_insn_aux_data *insn_aux,
const struct btf_type *ptr_type, struct btf *desc_btf)
{
@@ -12974,7 +12956,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
struct bpf_reg_state *regs = cur_regs(env);
const char *func_name, *ptr_type_name;
const struct btf_type *t, *ptr_type;
- struct bpf_kfunc_call_arg_meta meta;
+ struct bpf_call_arg_meta meta;
struct bpf_insn_aux_data *insn_aux;
int err, insn_idx = *insn_idx_p;
const struct btf_param *args;
@@ -16728,7 +16710,7 @@ bool bpf_verifier_inlines_helper_call(struct bpf_verifier_env *env, s32 imm)
bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,
struct bpf_call_summary *cs)
{
- struct bpf_kfunc_call_arg_meta meta;
+ struct bpf_call_arg_meta meta;
const struct bpf_func_proto *fn;
int i;
--
2.52.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH bpf-next v2 7/7] bpf: Unify helper and kfunc call argument meta
2026-07-15 6:40 ` [PATCH bpf-next v2 7/7] bpf: Unify helper and kfunc call argument meta Amery Hung
@ 2026-07-15 7:00 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-15 7:00 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] Uninitialized stack variable `meta.func_name` dereferenced on error path, leading to kernel panic.
--
--- Patch [7]: [PATCH bpf-next v2 7/7] bpf: Unify helper and kfunc call argument meta ---
commit ca91a3b2fc7adc980912af895a2a5840eb94b51f
Author: Amery Hung <ameryhung@gmail.com>
bpf: Unify helper and kfunc call argument meta
This commit merges bpf_call_arg_meta and bpf_kfunc_call_arg_meta into a single
struct. The unused btf_id field is dropped from the helper meta as groundwork
for sharing argument checking logic.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12953,11 +12953,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> struct bpf_reg_state *regs = cur_regs(env);
> const char *func_name, *ptr_type_name;
> const struct btf_type *t, *ptr_type;
> - struct bpf_kfunc_call_arg_meta meta;
> + struct bpf_call_arg_meta meta;
> struct bpf_insn_aux_data *insn_aux;
> int err, insn_idx = *insn_idx_p;
> const struct btf_param *args;
[ ... ]
> /* skip for now, but return error when we find this in fixup_kfunc_call */
> if (!insn->imm)
> return 0;
>
> err = bpf_fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta);
> if (err == -EACCES && meta.func_name)
> verbose(env, "calling kernel function %s is not allowed\n", meta.func_name);
[Severity: High]
This is a pre-existing issue, but does this error path dereference an
uninitialized stack variable?
Looking at bpf_fetch_kfunc_arg_meta() in kernel/bpf/verifier.c, if
fetch_kfunc_meta() returns an error like -EACCES, it returns early without
initializing the struct:
int bpf_fetch_kfunc_arg_meta(...)
{
...
err = fetch_kfunc_meta(env, func_id, offset, &kfunc);
if (err)
return err;
memset(meta, 0, sizeof(*meta));
...
}
When this early return happens, the meta struct on the stack in
check_kfunc_call() remains uninitialized. The check here for
err == -EACCES && meta.func_name will then evaluate garbage memory for
meta.func_name.
If the uninitialized memory evaluates to true, verbose() will dereference
that garbage pointer, which could lead to a kernel panic. Could we initialize
the meta struct earlier, or zero it in check_kfunc_call() before passing it?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715064047.1793790-1-ameryhung@gmail.com?part=7
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta
2026-07-15 6:40 [PATCH bpf-next v2 0/7] Unify helper and kfunc call_arg_meta Amery Hung
` (6 preceding siblings ...)
2026-07-15 6:40 ` [PATCH bpf-next v2 7/7] bpf: Unify helper and kfunc call argument meta Amery Hung
@ 2026-07-15 9:10 ` patchwork-bot+netdevbpf
7 siblings, 0 replies; 12+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-07-15 9:10 UTC (permalink / raw)
To: Amery Hung
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team
Hello:
This series was applied to bpf/bpf-next.git (master)
by Kumar Kartikeya Dwivedi <memxor@gmail.com>:
On Tue, 14 Jul 2026 23:40:40 -0700 you wrote:
> Hi all,
>
> I am working toward unifying helper and kfunc handling in the verifier.
> Historically, kfunc verification was mostly done separately from helper.
> This causes code/logic duplication and may introduce subtle bugs as the
> same checks are implemnented differently.
>
> [...]
Here is the summary with links:
- [bpf-next,v2,1/7] bpf: Remove dynptr check in check_stack_range_initialized()
https://git.kernel.org/bpf/bpf-next/c/c28cbef2f898
- [bpf-next,v2,2/7] bpf: Factor out raw_mode-related fields in bpf_call_arg_meta
https://git.kernel.org/bpf/bpf-next/c/92ec8b1b6b24
- [bpf-next,v2,3/7] bpf: Pass argno to callees in check_func_arg() instead of argno_from_reg(regno)
https://git.kernel.org/bpf/bpf-next/c/77a4974c1749
- [bpf-next,v2,4/7] bpf: Unify helper and kfunc allocation-size argument handling
https://git.kernel.org/bpf/bpf-next/c/8faaa93b9f6a
- [bpf-next,v2,5/7] selftests/bpf: Test kfunc returning zero-sized allocation buffer
https://git.kernel.org/bpf/bpf-next/c/1e63cd6be055
- [bpf-next,v2,6/7] bpf: Drop redundant pkt_access from bpf_call_arg_meta
https://git.kernel.org/bpf/bpf-next/c/d55149ff8c85
- [bpf-next,v2,7/7] bpf: Unify helper and kfunc call argument meta
https://git.kernel.org/bpf/bpf-next/c/bf9c1b911f4d
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 12+ messages in thread