* [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
* 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
* [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
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 an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.