From 007fd460798be9af7d4874734f934afa47742f29 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Sun, 1 Nov 2026 19:05:09 +0000 Subject: bpf: Clamp the trace length in __bpf_get_stack() again get_perf_callchain() and get_callchain_entry_for_task() store a trace in a per-CPU entry, one per context. They release the entry before they return it. Thus a second user in the same context can write to the entry while the first user reads it. preempt_disable() stops only other tasks. __bpf_get_stack() requests at most max_depth frames. This is skip plus the frames that fit in the buffer, but not more than perf_event_max_stack. Since the commit below, it copies 'trace->nr - skip' frames with no limit. But a second user can store a longer trace. The unwinders of PowerPC add context markers that max_depth does not limit. In both cases the copy writes after the end of the buffer. Read trace->nr one time. Clamp the value to max_depth before the skip test, as __bpf_get_stack_pe() does. Thus when max_depth is less than skip, the helper returns -EFAULT, and 'trace_nr - skip' does not underflow. The copied frames can still be those of the second user. Fixes: e17d62fedd10 ("bpf: Refactor stack map trace depth calculation into helper function") Signed-off-by: Josef Bacik Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-2-alexei.starovoitov@gmail.com --- kernel/bpf/stackmap.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index f7e8d766d128..7d90891cce0a 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -822,14 +822,20 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, crosstask, false, 0); } - if (unlikely(!trace) || trace->nr < skip) { - if (may_fault) - rcu_read_unlock(); - preempt_enable(); - goto err_fault; - } + if (unlikely(!trace)) + goto err_unlock; + + /* + * trace->nr can be above max_depth. The per-CPU entry is released + * already: a tracing program or an NMI which takes a callchain in the + * same context before the copy below reuses it. And the unwinders of + * PowerPC store context markers on top of max_depth. + */ + trace_nr = min_t(u32, READ_ONCE(trace->nr), max_depth); + if (trace_nr < skip) + goto err_unlock; - trace_nr = callchain_store(trace, trace->nr, buf, elem_size, flags); + trace_nr = callchain_store(trace, trace_nr, buf, elem_size, flags); /* trace should not be dereferenced after this point */ if (may_fault) @@ -838,6 +844,10 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, return callchain_finalize(buf, size, trace_nr, elem_size, flags, may_fault); +err_unlock: + if (may_fault) + rcu_read_unlock(); + preempt_enable(); err_fault: err = -EFAULT; clear: -- cgit v1.2.3 From a08ba2ab79dffd92c32d3f2dadfc7eba03625a2b Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:10 +0000 Subject: bpf: Clamp the trace length before the skip test in stackid_init() stackid_init() gives a bucket of the stack map min(trace_nr - skip, max_depth - skip) trace entries. Here max_depth is the size of a bucket plus skip, but at most perf_event_max_stack. Context markers can make trace_nr larger than that, so the skip test can pass when max_depth < skip. Then 'max_depth - skip' wraps, and the kernel can write after the bucket. Clamp trace_nr to max_depth before the skip test, as __bpf_get_stack_pe() does. The result changes only when skip >= max_depth. Then it is -EFAULT. Until now skip == max_depth could give the id of an empty trace. get_perf_callchain() releases the trace, so an NMI can change trace->nr. Use READ_ONCE() in bpf_get_stackid(), so the compiler reads it one time. Fixes: 23f852daa4ba ("bpf: Fix stackmap overflow check in __bpf_get_stackid()") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-3-alexei.starovoitov@gmail.com --- kernel/bpf/stackmap.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 7d90891cce0a..a19126f5db02 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -522,12 +522,14 @@ static int stackid_init(struct stackid *stackid, struct bpf_map *map, u32 skip = flags & BPF_F_SKIP_FIELD_MASK; u32 max_depth; + max_depth = stack_map_calculate_max_depth(map->value_size, stack_map_data_size(map), flags); + /* trace_nr can be above max_depth and max_depth below skip: clamp first */ + trace_nr = min_t(u32, trace_nr, max_depth); if (trace_nr <= skip) /* skipping more than usable stack trace */ return -EFAULT; - max_depth = stack_map_calculate_max_depth(map->value_size, stack_map_data_size(map), flags); - stackid->nr = min_t(u32, trace_nr - skip, max_depth - skip); + stackid->nr = trace_nr - skip; stackid->len = stackid->nr * sizeof(u64); stackid->ips = trace->ip + skip; stackid->hash = jhash2((const u32 *)stackid->ips, stackid->len / sizeof(u32), 0); @@ -642,7 +644,8 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, /* couldn't fetch the stack trace */ return -EFAULT; - err = stackid_fastpath(&stackid, map, trace, trace->nr, flags); + /* the entry is released already, an NMI can change trace->nr */ + err = stackid_fastpath(&stackid, map, trace, READ_ONCE(trace->nr), flags); if (err != -ENOENT) return err; -- cgit v1.2.3 From ca580b034380fa9a69cc973637d1859ec4f5b344 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:11 +0000 Subject: bpf: Refuse to map an arena at address 0 The first mmap() of an arena sets user_vm_start and user_vm_end. Each subsequent mmap() must use the same range. The value 0 in user_vm_start means that the range is not set, but a mapping can start at address 0. Then arena_map_mmap() does not check vm_start and sets user_vm_start again. The range tree counts pages from user_vm_start. With a new user_vm_start it reports allocated pages as free, and apply_range_set_cb() fails with -EBUSY. An arena at address 0 is not useful, because the verifier rejects all progs that refer to it. Return -EINVAL from arena_get_unmapped_area(). It runs before mmap() removes the old mappings in the range. Fixes: 317460317a02 ("bpf: Introduce bpf_arena.") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-4-alexei.starovoitov@gmail.com --- kernel/bpf/arena.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'kernel') diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index 3c61c181a6fa..ad8aab551093 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -650,6 +650,9 @@ static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long ad ret = mm_get_unmapped_area(filp, addr, len * 2, 0, flags); if (IS_ERR_VALUE(ret)) return ret; + if (!ret) + /* user_vm_start == 0 means that the user range is not set yet */ + return -EINVAL; if ((ret >> 32) == ((ret + len - 1) >> 32)) return ret; if (WARN_ON_ONCE(arena->user_vm_start)) -- cgit v1.2.3 From 52498f14c2d26d67a9bf06998b797900827a06ad Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:12 +0000 Subject: bpf: Don't let bpf_arena_free_pages() see a half-set user range If map_extra is 0, the first arena_map_mmap() sets user_vm_start and user_vm_end. It holds arena->lock, but bpf_arena_free_pages() reads both fields without this mutex. The kfunc relies on the verifier, which rejects a prog if user_vm_start of its arena is 0. But this check covers only the maps in used_maps. The kfunc accepts any trusted pointer to struct bpf_map. Thus the kfunc can run during arena_map_mmap() and read user_vm_start as 0 and user_vm_end as vma->vm_end. Then min() does not limit page_cnt to the arena. arena_free_range() unmaps and frees pages of other vmalloc areas. Read user_vm_start one time and return if it is 0. This value means that no range is set, because arena_get_unmapped_area() rejects address 0. If only user_vm_end is still 0, uaddr_end is 0 and the kfunc returns, as today. Fixes: 317460317a02 ("bpf: Introduce bpf_arena.") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-5-alexei.starovoitov@gmail.com --- kernel/bpf/arena.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index ad8aab551093..e331130662be 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -991,6 +991,8 @@ static int arena_free_range(struct bpf_arena *arena, long uaddr, long page_cnt) static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, bool sleepable) { + /* The first mmap() may be setting the user range right now */ + u64 user_vm_start = READ_ONCE(arena->user_vm_start); struct mem_cgroup *new_memcg, *old_memcg; u64 full_uaddr, uaddr_end; struct arena_free_span *s; @@ -998,8 +1000,8 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, /* only aligned lower 32-bit are relevant */ uaddr = (u32)uaddr; uaddr &= PAGE_MASK; - full_uaddr = clear_lo32(arena->user_vm_start) + uaddr; - if (full_uaddr < arena->user_vm_start) + full_uaddr = clear_lo32(user_vm_start) + uaddr; + if (!user_vm_start || full_uaddr < user_vm_start) return; uaddr_end = min(arena->user_vm_end, full_uaddr + (page_cnt << PAGE_SHIFT)); if (full_uaddr >= uaddr_end) -- cgit v1.2.3 From 1e01345b87f13c354687360cb81ed2a521ff2b13 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:13 +0000 Subject: bpf: Refuse an arena mmap() longer than max_entries pages arena_map_alloc() puts max_entries free pages in the range tree of an arena. But the arena kfuncs check their arguments against user_vm_start and user_vm_end. Without map_extra, arena_map_mmap() takes both from the first vma. This vma can be up to 4 GiB long. Then arena_free_pages() also marks pages as free that were never in the range tree. Thus max_entries does not limit the memory of the arena, but these pages are inside kern_vm. Refuse an mmap() longer than max_entries pages with -E2BIG. With map_extra this limit exists already. libbpf always maps max_entries pages. Replace both SZ_4G checks with this limit, because it is at most 4 GiB. Fixes: 317460317a02 ("bpf: Introduce bpf_arena.") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-6-alexei.starovoitov@gmail.com --- kernel/bpf/arena.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index e331130662be..c35b82f44f66 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -634,7 +634,7 @@ static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long ad if (pgoff) return -EINVAL; - if (len > SZ_4G) + if (len > (u64)map->max_entries * PAGE_SIZE) return -E2BIG; /* if user_vm_start was specified at arena creation time */ @@ -682,7 +682,7 @@ static int arena_map_mmap(struct bpf_map *map, struct vm_area_struct *vma) return -EBUSY; /* Earlier checks should prevent this */ - if (WARN_ON_ONCE(vma->vm_end - vma->vm_start > SZ_4G || vma->vm_pgoff)) + if (WARN_ON_ONCE(vma_pages(vma) > map->max_entries || vma->vm_pgoff)) return -EFAULT; if (remember_vma(arena, vma)) -- cgit v1.2.3 From 15dedd4b259289bffb8a5485b81722fdaebac71a Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:14 +0000 Subject: bpf: Remove the WARN from arena_get_unmapped_area() The user range of an arena must not cross a 4 GiB boundary. Thus arena_get_unmapped_area() asks the mm for 2 * len bytes and rounds the result up if necessary. If user_vm_start is not 0, addr must be equal to it. A WARN_ON_ONCE() assumes that the mm returns addr. But addr is only a hint without MAP_FIXED, and the mm can return a different range that crosses a boundary. This is not a kernel bug. Remove the WARN_ON_ONCE() and its -EFAULT. arena_map_mmap() already compares vm_start with user_vm_start under arena->lock. If the rounded result is user_vm_start, mmap() succeeds. If not, it returns -EBUSY. Fixes: 317460317a02 ("bpf: Introduce bpf_arena.") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-7-alexei.starovoitov@gmail.com --- kernel/bpf/arena.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index c35b82f44f66..0b1ec3306ea4 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -655,9 +655,6 @@ static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long ad return -EINVAL; if ((ret >> 32) == ((ret + len - 1) >> 32)) return ret; - if (WARN_ON_ONCE(arena->user_vm_start)) - /* checks at map creation time should prevent this */ - return -EFAULT; return round_up(ret, SZ_4G); } -- cgit v1.2.3 From f89ebf07496b041cb21beda60ec3452fecf34d10 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 1 Nov 2026 19:05:16 +0000 Subject: bpf: Reject memory for kfunc projection type args struct __sk_buff and struct xdp_md are projection types of struct sk_buff and struct xdp_buff. A kfunc that takes a projection type casts the argument to the underlying type. When the projection type is the ctx type of the prog, the verifier accepts only the ctx. In other progs, process_arg_ptr_to_btf_id() accepts a pointer to the underlying type. But this function identifies the underlying type only by its name. A struct of the same name in prog BTF also matches. Its objects are memory of the prog. This test also ignores the offset of the register. Thus the kfunc uses memory of the prog, or the middle of an skb, as an skb. Accept the underlying type only from kernel BTF, and only at offset 0. A prog that passes an skb of the kernel sees no change. struct xdp_md has only scalar members. Thus in a prog with another ctx type, resolve_func_arg_type() changes ARG_PTR_TO_BTF_ID to memory, and the verifier accepts memory of the prog. Keep ARG_PTR_TO_BTF_ID for projection types. The first Fixes tag is for this change. That commit merged the conntrack kfunc sets of two prog types with different ctx types. Thus bpf_xdp_ct_{alloc,lookup}() can use memory of the prog as an xdp_buff. Fixes: a4703e318432 ("bpf: Switch to new kfunc flags infrastructure") Fixes: ec209ad86324 ("bpf: verifier: Relax caller requirements for kfunc projection type args") Signed-off-by: Chris Mason Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-9-alexei.starovoitov@gmail.com --- kernel/bpf/btf.c | 9 +++++++++ kernel/bpf/verifier.c | 16 +++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 1be30487a2fa..fe24e459c1b1 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6590,6 +6590,15 @@ static int find_kern_ctx_type_id(enum bpf_prog_type prog_type) return ctx_type->type; } +/* + * A projection type is the view that programs have of a kernel object with + * another layout: see btf_is_projection_of() for the two types behind them. + */ +bool btf_is_projection_type(const char *pname) +{ + return strcmp(pname, "__sk_buff") == 0 || strcmp(pname, "xdp_md") == 0; +} + bool btf_is_projection_of(const char *pname, const char *tname) { if (strcmp(pname, "__sk_buff") == 0 && strcmp(tname, "sk_buff") == 0) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a90bc6c4033a..89c0f0304341 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13585,7 +13585,13 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env, ref_t = btf_type_skip_modifiers(btf, ref_id, &ref_id); ref_tname = btf_name_by_offset(btf, ref_t->name_off); - if (!btf_type_is_scalar_struct(env, btf, ref_t)) + /* + * A kfunc that takes a projection type (ie. xdp_md) casts it to the + * underlying type. So memory of the program cannot stand in for it, + * even if the projection type holds only scalars. + */ + if (btf_is_projection_type(ref_tname) || + !btf_type_is_scalar_struct(env, btf, ref_t)) return 0; resolve_ret = btf_resolve_size(btf, ref_t, arg_size); @@ -14340,9 +14346,13 @@ static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_re /* If kfunc is accepting a projection type (ie. __sk_buff), it cannot * actually use it -- it must cast to the underlying type. So we allow - * caller to pass in the underlying type. + * caller to pass in the underlying type. Only kernel BTF has that type: + * a type of the same name in the BTF of the program is program memory. + * And only a pointer to the start of the object points to that type. */ - taking_projection = is_kfunc(meta) && btf_is_projection_of(arg_tname, reg_tname); + taking_projection = is_kfunc(meta) && btf_is_kernel(reg_btf) && + !reg->var_off.value && + btf_is_projection_of(arg_tname, reg_tname); if (!taking_projection && !struct_same) { verbose(env, "%s %s expected pointer to %s %s but %s has a pointer to %s %s\n", meta->func_name, reg_arg_name(env, argno), -- cgit v1.2.3 From 07684726d39e56c1c800d3b3e51915fb0975a46d Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:17 +0000 Subject: bpf: Don't turn struct arguments of KF_ACQUIRE kfuncs into memory When a kfunc takes a pointer to a struct that has only scalar members, the verifier also accepts memory of the prog. Then resolve_func_arg_type() changes ARG_PTR_TO_BTF_ID to ARG_PTR_TO_MEM. This is correct for a struct of parameters. But struct bpf_cpumask also has only scalar members, and the kernel allocates and frees it. Thus bpf_cpumask_acquire() returns memory of the prog as a referenced object, and bpf_cpumask_release() can free it. The verifier does not change the argument that a kfunc releases. KF_ACQUIRE does not say which argument is the object. Thus do not change any argument of a KF_ACQUIRE kfunc. Then the verifier rejects memory there. No other KF_ACQUIRE kfunc takes memory in an ARG_PTR_TO_BTF_ID argument. Add to kfuncs.rst that a KF_ACQUIRE kfunc must pair a struct of parameters with a __sz argument. The first commit below added the bug. Later the struct had pointers in an rcu_head, so the verifier rejected memory. The second commit removed them. Fixes: 516f4d3397c9 ("bpf: Enable cpumasks to be queried and used as kptrs") Fixes: 8e07bb9ebcd9 ("bpf: Convert bpf_cpumask to bpf_mem_cache_free_rcu.") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-10-alexei.starovoitov@gmail.com --- kernel/bpf/verifier.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'kernel') diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 89c0f0304341..ac56e7642432 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13574,7 +13574,12 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env, if (base_type(*arg_type) != ARG_PTR_TO_BTF_ID) return 0; + /* + * A kfunc that releases an object, or that takes a reference to one, + * needs the object itself: memory of the program cannot stand in for it. + */ if (!is_kfunc(meta) || arg_type_is_release(*arg_type) || + is_kfunc_acquire(meta) || base_type(reg->type) == PTR_TO_BTF_ID || reg2btf_ids[base_type(reg->type)]) return 0; -- cgit v1.2.3 From ed02b9cc1d377d03da2507c3a858f0874aef7494 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:18 +0000 Subject: bpf: Don't turn kfunc arguments of an empty struct type into memory A kfunc can take a pointer to a struct that has only scalar members. Then the verifier also accepts memory of the prog: resolve_func_arg_type() changes ARG_PTR_TO_BTF_ID to memory of the size of the struct. An empty struct meets this condition too. But it is a handle for an object of another type. The io_uring kfuncs cast the empty struct iou_ctx to struct io_ring_ctx. Thus they use memory of the prog as a struct io_ring_ctx. Keep ARG_PTR_TO_BTF_ID for a pointer to an empty struct. Then the verifier accepts only a PTR_TO_BTF_ID of that struct, such as the ctx argument of loop_step(). Only one other kfunc takes an empty struct, struct bpf_ws_lock. It releases this argument, and the verifier does not change an argument that the kfunc releases. Add the rule to kfuncs.rst. Fixes: ec02fe217fa6 ("io_uring/bpf-ops: restrict ctx access to BPF") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-11-alexei.starovoitov@gmail.com --- kernel/bpf/verifier.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ac56e7642432..1bf38f3e4819 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13593,10 +13593,12 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env, /* * A kfunc that takes a projection type (ie. xdp_md) casts it to the * underlying type. So memory of the program cannot stand in for it, - * even if the projection type holds only scalars. + * even if the projection type holds only scalars. A struct without + * members (ie. iou_ctx) is a handle for an object of another type. */ if (btf_is_projection_type(ref_tname) || - !btf_type_is_scalar_struct(env, btf, ref_t)) + !btf_type_is_scalar_struct(env, btf, ref_t) || + !btf_type_vlen(ref_t)) return 0; resolve_ret = btf_resolve_size(btf, ref_t, arg_size); -- cgit v1.2.3 From faea3cda41d3deb4f28e7970c556896b1aa8bd76 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:19 +0000 Subject: bpf: Compare the arguments of an extension with those of its target An extension replaces its target, a global function of another prog. From BTF, btf_prepare_func_args() derives the kind of each argument, such as the context, memory of a given size or a kernel object. The verifier checks the callers with the BTF of the target and the extension with its own BTF. Only btf_check_func_type_match() compares the two prototypes. For a pointer to a struct, it compares only the struct names and assumes that the pointer is the context. That was true until the commit below added pointers to memory. Later the __arg_* tags added more kinds. Thus an extension can rely on more than the callers guarantee (size, not NULL, kind). Then what the verifier proved for it is false at run time. Keep the result of btf_prepare_func_args() in bpf_func_info_aux and compare the two results at load and on attach to another target. Return -EINVAL unless the kind and the BTF id are the same and the memory is not larger. The extension can also accept NULL where the callers never pass it. An extension with the prototype and the tags of its target loads as before. An argument that is the context only because of __arg_ctx needs the tag in the extension too. The tag __arg_untrusted or __arg_arena on the extension only is safe, but it is rejected because the kind differs. Fixes: e5069b9c23b3 ("bpf: Support pointers in global func args") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-12-alexei.starovoitov@gmail.com --- kernel/bpf/btf.c | 10 +++++++--- kernel/bpf/verifier.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index fe24e459c1b1..8bf39098ba97 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -8492,9 +8492,8 @@ static int btf_check_func_type_match(struct bpf_verifier_log *log, } /* This is an optional check to make program writing easier. * Compare names of structs and report an error to the user. - * btf_prepare_func_args() already checked that t2 struct - * is a context type. btf_prepare_func_args() will check - * later that t1 struct is a context type as well. + * check_ext_args() compares what btf_prepare_func_args() + * derives for the two arguments. */ s1 = btf_name_by_offset(btf1, t1->name_off); s2 = btf_name_by_offset(btf2, t2->name_off); @@ -9028,6 +9027,11 @@ skip_pointer: return err; sub->arg_slot_cnt = slots_used; + /* check_ext_args() needs them after env is freed */ + if (is_global) + memcpy(subprog_aux(env, subprog)->args, sub->args, + sizeof(subprog_aux(env, subprog)->args)); + sub->args_cached = true; return 0; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1bf38f3e4819..217bcfa6ebfe 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -22527,6 +22527,37 @@ static bool func_has_rcu_read_lock(const struct bpf_prog *prog, int subprog) !sub_aux->called[BPF_FUNC_CTX_NO_RCU_LOCK]; } +/* + * The callers of a global function are checked against the arguments that its + * BTF describes, and an extension is verified with those of its own BTF. It + * must not expect more than the callers of the function provide. + * + * While an extension is loaded, bpf_check_attach_target() runs before its + * arguments are known. check_attach_btf_id() does the check when they are. + */ +static int check_ext_args(struct bpf_verifier_log *log, const struct bpf_prog *prog, + const struct bpf_prog *tgt_prog, int subprog) +{ + const struct bpf_subprog_arg_info *tgt = tgt_prog->aux->func_info_aux[subprog].args; + const struct bpf_subprog_arg_info *ext = prog->aux->func_info_aux[0].args; + int i; + + for (i = 0; i < MAX_BPF_FUNC_REG_ARGS && ext[i].arg_type != ARG_UNUSED; i++) { + /* An extension may accept NULL where the callers never pass it. */ + if ((ext[i].arg_type == tgt[i].arg_type || + ext[i].arg_type == (tgt[i].arg_type | PTR_MAYBE_NULL)) && + (base_type(ext[i].arg_type) == ARG_PTR_TO_MEM ? + ext[i].mem_size <= tgt[i].mem_size : ext[i].btf_id == tgt[i].btf_id)) + continue; + + bpf_log(log, "Extension program's argument in R%d is incompatible with original\n", + i + 1); + return -EINVAL; + } + + return 0; +} + int bpf_check_attach_target(struct bpf_verifier_log *log, const struct bpf_prog *prog, const struct bpf_prog *tgt_prog, @@ -22814,7 +22845,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, return -EINVAL; } if (prog_extension && - btf_check_type_match(log, prog, btf, t)) + (btf_check_type_match(log, prog, btf, t) || + check_ext_args(log, prog, tgt_prog, subprog))) return -EINVAL; t = btf_attach_func_proto(log, btf, btf_id); if (!t || !btf_type_is_func_proto(t)) @@ -23034,6 +23066,15 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) */ env->ops = bpf_verifier_ops[tgt_prog->type]; prog->expected_attach_type = tgt_prog->expected_attach_type; + + /* only now it is known what the arguments of prog stand for */ + ret = btf_prepare_func_args(env, 0); + if (ret) + return ret; + ret = check_ext_args(&env->log, prog, tgt_prog, + find_tgt_subprog(tgt_prog, btf_id)); + if (ret) + return ret; } /* store info about the attachment target that will be used later */ -- cgit v1.2.3 From 2962329f7c86fc51c177d4049c0f15a95ebd270c Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 1 Nov 2026 19:05:21 +0000 Subject: bpf: Require prog fd or capability for cgroup detach Since the commit below a cgroup prog detach does not require CAP_NET_ADMIN. The fd of the attached prog gives the permission instead. But the detach requires this fd only with BPF_F_ALLOW_MULTI. Without this flag a cgroup has only one prog per attach type. Then find_detach_entry() returns this prog and does not compare it with the prog of the caller. Thus the kernel detaches this prog for a caller with neither the fd nor a capability. Compare the two progs. If they are different, require CAP_NET_ADMIN or CAP_SYS_ADMIN in the initial user namespace. The error is -EPERM. A detach from tcx or netkit without a prog fd already requires these capabilities. With BPF_F_ALLOW_MULTI nothing changes. Without these capabilities, a caller of bpf_prog_detach() of libbpf must now use bpf_prog_detach2() with the fd of the attached prog. Fixes: 2c78ee898d8f ("bpf: Implement CAP_BPF") Signed-off-by: Chris Mason Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-14-alexei.starovoitov@gmail.com --- kernel/bpf/cgroup.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index ce82963bcfc7..ca681d626c9e 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1279,9 +1279,14 @@ static struct bpf_prog_list *find_detach_entry(struct hlist_head *progs, return ERR_PTR(-ENOENT); /* to maintain backward compatibility NONE and OVERRIDE cgroups - * allow detaching with invalid FD (prog==NULL) in legacy mode + * allow detaching with invalid FD (prog==NULL) in legacy mode. + * Without the FD of the attached program the caller needs + * CAP_NET_ADMIN or CAP_SYS_ADMIN in the initial user namespace. */ - return hlist_entry(progs->first, typeof(*pl), node); + pl = hlist_entry(progs->first, typeof(*pl), node); + if (pl->prog != prog && !bpf_net_capable()) + return ERR_PTR(-EPERM); + return pl; } if (!prog && !link) -- cgit v1.2.3 From 3f9780d62dd1112ca15b5ae73568d29d42ccc950 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 1 Nov 2026 19:05:22 +0000 Subject: bpf, cgroup: Keep the skb network offset signed cgroup_skb progs see a packet from its network header. While they run, __cgroup_bpf_run_filter_skb() moves skb->data there. It assumes that the network header is not after skb->data. It negates skb_network_offset(skb) and pushes skb->data by this offset. The offset is an unsigned int. But ip6mr_cache_report() and bpf_lwt_xmit_reroute() can pass an skb that has data before the network header. Then the offset wraps. On a 64-bit kernel skb->data is then far below skb->head. skb->len has 32 bits and wraps to the correct value, thus only skb->data is wrong. A read of packet data faults or gets other memory. Keep the offset in an int. Pull skb->data when the network header is after it. Otherwise push it, as today. Fixes: 3007098494be ("cgroup: add support for eBPF programs") Signed-off-by: Chris Mason Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-15-alexei.starovoitov@gmail.com --- kernel/bpf/cgroup.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index ca681d626c9e..57a785241d42 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1790,7 +1790,7 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb, enum cgroup_bpf_attach_type atype) { - unsigned int offset = -skb_network_offset(skb); + int offset = skb_network_offset(skb); struct sock *save_sk; void *saved_data_end; struct cgroup *cgrp; @@ -1802,7 +1802,15 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); save_sk = skb->sk; skb->sk = sk; - __skb_push(skb, offset); + + /* + * The program sees the packet from its network header on, on + * whichever side of skb->data that header lies. + */ + if (offset > 0) + __skb_pull(skb, offset); + else + __skb_push(skb, -offset); /* compute pointers for the bpf prog */ bpf_compute_and_save_data_end(skb, &saved_data_end); @@ -1844,7 +1852,10 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, ret = -EFAULT; } bpf_restore_data_end(skb, saved_data_end); - __skb_pull(skb, offset); + if (offset > 0) + __skb_push(skb, offset); + else + __skb_pull(skb, -offset); skb->sk = save_sk; return ret; -- cgit v1.2.3