* [PATCH bpf-next v2 0/2] bpf: Reject MEM_ALLOC BTF accesses past bounds
@ 2026-06-23 17:34 Yiyang Chen
2026-06-23 17:34 ` [PATCH bpf-next v2 1/2] bpf: Reject MEM_ALLOC BTF accesses past object bounds Yiyang Chen
2026-06-23 17:34 ` [PATCH bpf-next v2 2/2] selftests/bpf: Cover MEM_ALLOC access " Yiyang Chen
0 siblings, 2 replies; 5+ messages in thread
From: Yiyang Chen @ 2026-06-23 17:34 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi
Cc: Yiyang Chen, John Fastabend, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, bpf,
linux-kselftest, linux-kernel
BTF struct walks can relax the top-level struct-size check for trailing
flexible arrays. That relaxation must not let a PTR_TO_BTF_ID | MEM_ALLOC
access escape the bytes allocated by bpf_obj_new() or bpf_percpu_obj_new().
Patch 1 rejects MEM_ALLOC BTF walks whose access range reaches past the
current struct size before applying the flexible-array relaxation. Patch 2
adds a linked_list negative loader case for this path.
Changes in v2:
- Move the check from bpf_obj_new() type validation to BTF struct walking.
- Reject MEM_ALLOC accesses that reach past the allocated object bounds.
- Update the selftest expected verifier error and carry Eduard's Acked-by.
v1:
https://lore.kernel.org/bpf/cover.1782100805.git.chenyy23@mails.tsinghua.edu.cn/
Yiyang Chen (2):
bpf: Reject MEM_ALLOC BTF accesses past object bounds
selftests/bpf: Cover MEM_ALLOC access past object bounds
kernel/bpf/btf.c | 14 +++++++----
.../selftests/bpf/prog_tests/linked_list.c | 1 +
.../selftests/bpf/progs/linked_list_fail.c | 23 +++++++++++++++++++
3 files changed, 33 insertions(+), 5 deletions(-)
base-commit: a975094bf98ca97be9146f9d3b5681a6f9cf5ce3
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH bpf-next v2 1/2] bpf: Reject MEM_ALLOC BTF accesses past object bounds
2026-06-23 17:34 [PATCH bpf-next v2 0/2] bpf: Reject MEM_ALLOC BTF accesses past bounds Yiyang Chen
@ 2026-06-23 17:34 ` Yiyang Chen
2026-06-23 17:51 ` sashiko-bot
2026-06-23 17:34 ` [PATCH bpf-next v2 2/2] selftests/bpf: Cover MEM_ALLOC access " Yiyang Chen
1 sibling, 1 reply; 5+ messages in thread
From: Yiyang Chen @ 2026-06-23 17:34 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi
Cc: Yiyang Chen, John Fastabend, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, bpf,
linux-kselftest, linux-kernel
BTF struct walks relax the struct-size check for accesses through a
trailing flexible array. That is valid for ordinary BTF type walking, but
PTR_TO_BTF_ID | MEM_ALLOC values point to objects allocated with the static
BTF type size.
When walking a MEM_ALLOC object, reject the access before applying the
flexible-array relaxation if the access range extends past the struct size.
This keeps verifier-approved BTF accesses within the bytes provided by the
allocation kfunc.
Fixes: 958cf2e273f0 ("bpf: Introduce bpf_obj_new")
Fixes: 36d8bdf75a93 ("bpf: Add alloc/xchg/direct_access support for local percpu kptr")
Signed-off-by: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
---
kernel/bpf/btf.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 15ae7c43f..3e68af9c1 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7069,7 +7069,7 @@ enum bpf_struct_walk_result {
static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
const struct btf_type *t, int off, int size,
u32 *next_btf_id, enum bpf_type_flag *flag,
- const char **field_name)
+ const char **field_name, bool is_alloc)
{
u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
const struct btf_type *mtype, *elem_type = NULL;
@@ -7096,11 +7096,14 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
*flag |= PTR_UNTRUSTED;
if (off + size > t->size) {
+ struct btf_array *array_elem;
+
+ if (is_alloc)
+ goto error;
+
/* If the last element is a variable size array, we may
* need to relax the rule.
*/
- struct btf_array *array_elem;
-
if (vlen == 0)
goto error;
@@ -7363,7 +7366,8 @@ int btf_struct_access(struct bpf_verifier_log *log,
t = btf_type_by_id(btf, id);
do {
- err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag, field_name);
+ err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag,
+ field_name, type_is_alloc(reg->type));
switch (err) {
case WALK_PTR:
@@ -7441,7 +7445,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
type = btf_type_by_id(btf, id);
if (!type)
return false;
- err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL);
+ err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL, false);
if (err != WALK_STRUCT)
return false;
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH bpf-next v2 2/2] selftests/bpf: Cover MEM_ALLOC access past object bounds
2026-06-23 17:34 [PATCH bpf-next v2 0/2] bpf: Reject MEM_ALLOC BTF accesses past bounds Yiyang Chen
2026-06-23 17:34 ` [PATCH bpf-next v2 1/2] bpf: Reject MEM_ALLOC BTF accesses past object bounds Yiyang Chen
@ 2026-06-23 17:34 ` Yiyang Chen
1 sibling, 0 replies; 5+ messages in thread
From: Yiyang Chen @ 2026-06-23 17:34 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi
Cc: Yiyang Chen, John Fastabend, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, bpf,
linux-kselftest, linux-kernel
Add a linked_list negative loader case for a program-BTF type whose last
member is a zero-length flexible array. The program writes through the
first flexible-array element of an object allocated by bpf_obj_new().
The verifier should reject the access when the BTF walk reaches beyond the
static size of the allocated object.
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
---
.../selftests/bpf/prog_tests/linked_list.c | 1 +
.../selftests/bpf/progs/linked_list_fail.c | 23 +++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c
index 8defea025..c3d133c6a 100644
--- a/tools/testing/selftests/bpf/prog_tests/linked_list.c
+++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c
@@ -68,6 +68,7 @@ static struct {
{ "obj_type_id_oor", "local type ID argument must be in range [0, U32_MAX]" },
{ "obj_new_no_composite", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" },
{ "obj_new_no_struct", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" },
+ { "obj_new_flex_array", "access beyond struct obj_new_flex" },
{ "obj_drop_non_zero_off", "R1 must have zero offset when passed to release func" },
{ "new_null_ret", "R0 invalid mem access 'ptr_or_null_'" },
{ "obj_new_acq", "Unreleased reference id=" },
diff --git a/tools/testing/selftests/bpf/progs/linked_list_fail.c b/tools/testing/selftests/bpf/progs/linked_list_fail.c
index ddd26d1a0..031e77a28 100644
--- a/tools/testing/selftests/bpf/progs/linked_list_fail.c
+++ b/tools/testing/selftests/bpf/progs/linked_list_fail.c
@@ -167,6 +167,16 @@ CHECK_OP(push_back);
#undef CHECK_OP
#undef INIT
+struct obj_new_flex_elem {
+ int lo;
+ int hi;
+};
+
+struct obj_new_flex {
+ int hdr;
+ struct obj_new_flex_elem cells[];
+};
+
SEC("?kprobe/xyz")
int map_compat_kprobe(void *ctx)
{
@@ -230,6 +240,19 @@ int obj_new_no_struct(void *ctx)
return 0;
}
+SEC("?tc")
+int obj_new_flex_array(void *ctx)
+{
+ struct obj_new_flex *p;
+
+ p = bpf_obj_new_impl(bpf_core_type_id_local(struct obj_new_flex), NULL);
+ if (!p)
+ return 0;
+ p->cells[0].hi = 42;
+ bpf_obj_drop_impl(p, NULL);
+ return 0;
+}
+
SEC("?tc")
int obj_drop_non_zero_off(void *ctx)
{
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpf: Reject MEM_ALLOC BTF accesses past object bounds
2026-06-23 17:34 ` [PATCH bpf-next v2 1/2] bpf: Reject MEM_ALLOC BTF accesses past object bounds Yiyang Chen
@ 2026-06-23 17:51 ` sashiko-bot
2026-06-23 22:41 ` Amery Hung
0 siblings, 1 reply; 5+ messages in thread
From: sashiko-bot @ 2026-06-23 17:51 UTC (permalink / raw)
To: Yiyang Chen; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] btf_struct_ids_match() hardcodes `is_alloc=false`, bypassing the MEM_ALLOC bounds protection and allowing out-of-bounds kfunc accesses.
--
commit ea4066197ea8dc72df56252849c8752b81c506d6
Author: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
bpf: Reject MEM_ALLOC BTF accesses past object bounds
This patch modifies the BTF struct walk logic to reject memory accesses past
the object bounds when dealing with MEM_ALLOC objects. It disables the
trailing flexible array relaxation for these allocated objects, keeping
verifier-approved BTF accesses strictly within the bytes provided by the
allocation kfunc.
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 15ae7c43f594b..3e68af9c1a5eb 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
[ ... ]
> @@ -7441,7 +7445,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
> type = btf_type_by_id(btf, id);
> if (!type)
> return false;
> - err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL);
> + err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL, false);
[Severity: High]
This isn't a bug introduced by this patch, but hardcoding false for the
is_alloc parameter here completely bypasses the newly introduced strict
bounds check for MEM_ALLOC types during struct matching.
Can this allow a BPF program to obtain a pointer to unallocated slab memory
and pass it to a kernel kfunc or store it in a map kptr?
For example, in kernel/bpf/verifier.c:check_kfunc_args(), it calls
btf_struct_ids_match() with reg->var_off.value without conveying that
the register possesses the MEM_ALLOC flag:
kernel/bpf/verifier.c:check_kfunc_args() {
...
struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id,
reg->var_off.value, meta->btf,
ref_id, strict_type_match);
...
}
A similar path exists in map_kptr_match_type() when storing into
unreferenced kptrs (where strict is false).
Does btf_struct_ids_match() need a way to know if the register type is
MEM_ALLOC so it can properly apply the bounds protection here as well?
> if (err != WALK_STRUCT)
> return false;
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1782197377.git.chenyy23@mails.tsinghua.edu.cn?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpf: Reject MEM_ALLOC BTF accesses past object bounds
2026-06-23 17:51 ` sashiko-bot
@ 2026-06-23 22:41 ` Amery Hung
0 siblings, 0 replies; 5+ messages in thread
From: Amery Hung @ 2026-06-23 22:41 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Yiyang Chen, bpf
On Tue, Jun 23, 2026 at 10:58 AM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] btf_struct_ids_match() hardcodes `is_alloc=false`, bypassing the MEM_ALLOC bounds protection and allowing out-of-bounds kfunc accesses.
> --
>
> commit ea4066197ea8dc72df56252849c8752b81c506d6
> Author: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
>
> bpf: Reject MEM_ALLOC BTF accesses past object bounds
>
> This patch modifies the BTF struct walk logic to reject memory accesses past
> the object bounds when dealing with MEM_ALLOC objects. It disables the
> trailing flexible array relaxation for these allocated objects, keeping
> verifier-approved BTF accesses strictly within the bytes provided by the
> allocation kfunc.
>
> > diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> > index 15ae7c43f594b..3e68af9c1a5eb 100644
> > --- a/kernel/bpf/btf.c
> > +++ b/kernel/bpf/btf.c
>
> [ ... ]
>
> > @@ -7441,7 +7445,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
> > type = btf_type_by_id(btf, id);
> > if (!type)
> > return false;
> > - err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL);
> > + err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL, false);
>
> [Severity: High]
> This isn't a bug introduced by this patch, but hardcoding false for the
> is_alloc parameter here completely bypasses the newly introduced strict
> bounds check for MEM_ALLOC types during struct matching.
>
> Can this allow a BPF program to obtain a pointer to unallocated slab memory
> and pass it to a kernel kfunc or store it in a map kptr?
>
> For example, in kernel/bpf/verifier.c:check_kfunc_args(), it calls
> btf_struct_ids_match() with reg->var_off.value without conveying that
> the register possesses the MEM_ALLOC flag:
>
> kernel/bpf/verifier.c:check_kfunc_args() {
> ...
> struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id,
> reg->var_off.value, meta->btf,
> ref_id, strict_type_match);
> ...
> }
>
> A similar path exists in map_kptr_match_type() when storing into
> unreferenced kptrs (where strict is false).
>
> Does btf_struct_ids_match() need a way to know if the register type is
> MEM_ALLOC so it can properly apply the bounds protection here as well?
>
> > if (err != WALK_STRUCT)
> > return false;
> >
>
Sashiko has a valid point. While hardcoding is_alloc=false in
btf_struct_ids_match() will likely not introduce triggerable bugs, it
is logically incorrect and fragile. I think it will need to come from
btf_struct_ids_match() callers.
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1782197377.git.chenyy23@mails.tsinghua.edu.cn?part=1
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-23 22:41 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 17:34 [PATCH bpf-next v2 0/2] bpf: Reject MEM_ALLOC BTF accesses past bounds Yiyang Chen
2026-06-23 17:34 ` [PATCH bpf-next v2 1/2] bpf: Reject MEM_ALLOC BTF accesses past object bounds Yiyang Chen
2026-06-23 17:51 ` sashiko-bot
2026-06-23 22:41 ` Amery Hung
2026-06-23 17:34 ` [PATCH bpf-next v2 2/2] selftests/bpf: Cover MEM_ALLOC access " Yiyang Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox