summaryrefslogtreecommitdiff
path: root/kernel/bpf
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-23 13:08:49 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-23 13:08:49 -1000
commit55fbedcec622a943cb09a969aef1ddcba87ee40a (patch)
tree99bf57398ef32396f831f31efffb5d917ce22e0c /kernel/bpf
parent90b754d40117b2889bbe847ce7bb4dcf968e7cb8 (diff)
parentb116ac6087541d3d98031e786adbe9c733cd6df3 (diff)
Merge tag 'bpf-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Pull bpf updates from Alexei Starovoitov: "Major changes: - Introduce scalar evolution (SCEV) analysis in the verifier. It infers how registers and stack slots change in a loop and lets the verifier check bounded loops with induction variables as ranges instead of walking every iteration. The number of processed insns drops ten fold in programs with loops (Eduard Zingerman) - Add 'callx' instruction: indirect call of a static BPF subprogram. The address is taken with ld_imm64 or loaded from read-only data. Supported by x86-64, arm64 and riscv64 JITs (Alexei Starovoitov, Pu Lehui) - Add unwind_goto and unwind_exit instructions, so that Rust panic or C++ exception can run destructors of every frame it passes through. The normal path is free of unwind code. Supported by x86-64 and arm64 JITs (Alexei Starovoitov) - Support programs compiled by rust-bpf: treat load and store through a number as arena access with BPF_F_ARENA_SCALAR, accept rust names in BTF (Alexei Starovoitov) - Raise BPF program stack size from 512 bytes to 2 KiB on x86-64 and arm64 (Kumar Kartikeya Dwivedi) - Support struct, union and __int128 up to 16 bytes passed and returned by value in BPF subprograms and kfuncs, and fix x86-64 and arm64 trampolines for such arguments (Yonghong Song) - Introduce a common way to attach struct_ops to a cgroup via bpf_link. bpf_tcp_ops is the first user and mirrors TCP sock_ops callbacks (Martin KaFai Lau, Amery Hung) - Describe inlined functions in BTF. New kinds tell where the inline sites are and how to fetch the arguments there. CONFIG_DEBUG_INFO_BTF_INLINE keeps it apart from the main BTF in .BTF.inline section or in btf_vmlinux_inline.ko (Alan Maguire) - Fix many memory safety, lifetime and locking bugs in BPF core, verifier, iterators, local storage, dynptr, devmap, cpumap and seg6local (Josef Bacik, Alexei Starovoitov) - Diff stat is dominated by selftests and by removal of lib/test_bpf.c. Its 16k lines of instruction tests were converted to BPF selftests that BPF CI runs on every architecture it covers (Daniel Borkmann) Other features and fixes: - Add KASAN checks to memory accesses of JITed programs on x86-64 (Alexis Lothoré) - Complete unification of helper, kfunc and subprog argument checks in the verifier (Amery Hung) - Add bpf_copy_from_user_mm[_str]() kfuncs (Anastasios Papagiannis) - Various features and fixes (Daniel Borkmann): - Add .bpf keyring for program signature verification, modelled after dm-verity keyring, and support ML-DSA in signing tools - Emit shorter and faster code in x86-64 JIT for patterns that LLVM generates - Fix various issues in gotox handling - Let BPF LSM set xattrs at inode creation with bpf_inode_init_xattr() (David Windsor, Daniel Borkmann) - Use sleepable page allocation in sleepable arena paths. Fix races between arena free, allocation and user page fault (Emil Tsalapatis) - Switch BPF crypto from lskcipher to AES-CBC and AES-ECB libraries (Eric Biggers) - Add bpf_proactive_reclaim() kfunc (Hui Zhu) - Render decl_tags in libbpf btf_dump and add tests for bpftool C dump (Ihor Solodrai) - Speed up tracing_multi attach on x86 (Jiri Olsa) - Various features and fixes (Kumar Kartikeya Dwivedi): - Add BPF_PROG_STREAM_OPEN command to read BPF streams through a file descriptor with poll support - Let kernel structs publish arena pointers with __arena type tag - Fix __uninit kfunc output buffers and fastcall rewrite with indirect stack accesses - Refuse CO-RE relocations for signed programs - Add enqueue_rcvq and dequeue_rcvq hooks to bpf_tcp_ops to adjust sk_rcvlowat and avoid unnecessary wakeups (Kuniyuki Iwashima) - Add bpf_ksock_sendto() kfunc and allow ksock connect and send from bpf_wq (Mahe Tardy) - Fix racy plt target update in arm64 bpf_arch_text_poke() (Matthew Wood) - Restructure BPF selftests Makefile as a layered build (Mykola Lysenko) - Speed up hash map lookups for u32 and u64 keys (Mykyta Yatsenko) - Add signed div/mod, movsx, bswap and gotol to MIPS JITs, signed div/mod to parisc JITs (Nicholas Dudar, Johan Almbladh) - Fix stack overflow in build ID parser on 32-bit (Nickolai Zeldovich) - Add stack arguments, signed arena loads and gotox to riscv64 JIT. Pu Lehui is now co-maintainer of riscv BPF JITs (Pu Lehui, Chen Pei, Feng Jiang) - Add bpf_call_rcu() and bpf_call_rcu_tasks_trace() kfuncs. Support fetching and/or/xor atomics in arena on x86-64 (Puranjay Mohan) - Fix flaky selftests, selftests with clang 23 and bpf-gcc (Qiliang Yuan, KaFai Wan, Vineet Gupta) - Fix builds broken by fatal resolve_btfids warnings, BTF generation on ARC, and define arch_cmpxchg_local() on arc, hexagon and openrisc (Sasha Levin) - Reduce memory use of hash maps by up to 50% per element (T.J. Mercier) - Mark pointer fields of sched_ext ops arguments as trusted (Tejun Heo) - Remove legacy bpf_asm, bpf_dbg and bpf_jit_disasm tools (Xia Yan) - Fix index overflow in queue and stack maps, arena range tree after allocation failure, tail call loop after freplace detach, lost signal in bpf_send_signal() (Yuan Chen) - Many more fixes and cleanups" * tag 'bpf-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (660 commits) selftests/bpf: Time tc_edt from its first packet with data selftests/bpf: Test unwind_exit of an exception callback selftests/bpf: Tests for 32-bit scalar evolution bounds computation bpf: Reject unwind_goto and unwind_exit in offloaded progs bpf: Support 32-bit linear scalar evolution bpf: Make unwind_exit of an exception callback exit in JITs selftests/bpf: Test forbidden bpf_ksock_sendto() LSM attach selftests/bpf: Test ksock sendto() bpf: Add bpf_ksock_sendto() kfunc selftests/bpf: Test unwinding from R0: R2 subprog selftests/bpf: Add a C++ prog with exceptions selftests/bpf: Add tests for unwind_goto and unwind_exit bpf, arm64: Support unwind_goto and unwind_exit bpf, x86: Support unwind_goto and unwind_exit bpf: Add unwind_goto and unwind_exit insns tools/bpf: Remove legacy BPF tools bpf: Annotate data races on bpf_lru_node type selftests/bpf: Add arena allocation race tests bpf: Fix arena race between page free and alloc leading to incoherency bpf: Revalidate faulted arena pages before installing user mappings ...
Diffstat (limited to 'kernel/bpf')
-rw-r--r--kernel/bpf/Kconfig26
-rw-r--r--kernel/bpf/Makefile10
-rw-r--r--kernel/bpf/arena.c408
-rw-r--r--kernel/bpf/arraymap.c8
-rw-r--r--kernel/bpf/backtrack.c158
-rw-r--r--kernel/bpf/bpf_insn_array.c19
-rw-r--r--kernel/bpf/bpf_iter.c31
-rw-r--r--kernel/bpf/bpf_local_storage.c66
-rw-r--r--kernel/bpf/bpf_lru_list.c9
-rw-r--r--kernel/bpf/bpf_lsm.c26
-rw-r--r--kernel/bpf/bpf_lsm_proto.c19
-rw-r--r--kernel/bpf/bpf_struct_ops.c142
-rw-r--r--kernel/bpf/btf.c1309
-rw-r--r--kernel/bpf/btf_vmlinux_inline.c29
-rw-r--r--kernel/bpf/cfg.c361
-rw-r--r--kernel/bpf/cgroup.c500
-rw-r--r--kernel/bpf/cnum.c36
-rw-r--r--kernel/bpf/cnum_defs.h81
-rw-r--r--kernel/bpf/const_fold.c10
-rw-r--r--kernel/bpf/core.c271
-rw-r--r--kernel/bpf/cpumap.c35
-rw-r--r--kernel/bpf/crypto.c288
-rw-r--r--kernel/bpf/devmap.c1
-rw-r--r--kernel/bpf/diagnostics.c71
-rw-r--r--kernel/bpf/diagnostics.h1
-rw-r--r--kernel/bpf/disasm.c12
-rw-r--r--kernel/bpf/fixups.c555
-rw-r--r--kernel/bpf/hashtab.c422
-rw-r--r--kernel/bpf/helpers.c322
-rw-r--r--kernel/bpf/keys.c73
-rw-r--r--kernel/bpf/liveness.c1075
-rw-r--r--kernel/bpf/log.c32
-rw-r--r--kernel/bpf/loops.c616
-rw-r--r--kernel/bpf/map_in_map.c11
-rw-r--r--kernel/bpf/map_iter.c83
-rw-r--r--kernel/bpf/offload.c25
-rw-r--r--kernel/bpf/queue_stack_maps.c3
-rw-r--r--kernel/bpf/range_tree.c52
-rw-r--r--kernel/bpf/ringbuf.c4
-rw-r--r--kernel/bpf/scev.c2916
-rw-r--r--kernel/bpf/states.c326
-rw-r--r--kernel/bpf/stream.c273
-rw-r--r--kernel/bpf/syscall.c146
-rw-r--r--kernel/bpf/sysfs_btf.c149
-rw-r--r--kernel/bpf/task_iter.c12
-rw-r--r--kernel/bpf/tnum.c11
-rw-r--r--kernel/bpf/token.c5
-rw-r--r--kernel/bpf/trampoline.c48
-rw-r--r--kernel/bpf/verifier.c6271
49 files changed, 13761 insertions, 3596 deletions
diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index eb3de35734f0..98493e32db2a 100644
--- a/kernel/bpf/Kconfig
+++ b/kernel/bpf/Kconfig
@@ -17,6 +17,10 @@ config HAVE_CBPF_JIT
config HAVE_EBPF_JIT
bool
+# KASAN support for JIT compiler
+config HAVE_EBPF_JIT_KASAN
+ bool
+
# Used by archs to tell that they want the BPF JIT compiler enabled by
# default for kernels that were compiled with BPF JIT support.
config ARCH_WANT_DEFAULT_BPF_JIT
@@ -87,6 +91,15 @@ config BPF_UNPRIV_DEFAULT_OFF
If you are unsure how to answer this question, answer Y.
+config BPF_CRYPTO
+ def_bool y
+ depends on BPF_SYSCALL
+ select CRYPTO_LIB_AES_CBC
+ select CRYPTO_LIB_AES_ECB
+ help
+ Provide the kfuncs needed for BPF programs to encrypt and decrypt
+ data. The supported algorithms are AES-CBC and AES-ECB.
+
source "kernel/bpf/preload/Kconfig"
config BPF_LSM
@@ -101,4 +114,17 @@ config BPF_LSM
If you are unsure how to answer this question, answer N.
+config BPF_JIT_KASAN
+ bool
+ depends on HAVE_EBPF_JIT_KASAN
+ depends on KASAN_GENERIC
+ depends on KASAN_VMALLOC
+ depends on BPF_JIT
+ default y
+ help
+ Makes JIT compiler insert generic outline KASAN checks in BPF
+ programs when they are inserted in the kernel. This feature is
+ automatically enabled if the needed set of KASAN and BPF
+ configuration options is enabled.
+
endmenu # "BPF subsystem"
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 90255d80e5be..269810b128c4 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -6,7 +6,7 @@ cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse
endif
CFLAGS_core.o += -Wno-override-init $(cflags-nogcse-yy)
-obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cnum.o log.o token.o liveness.o const_fold.o diagnostics.o
+obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cnum.o log.o token.o liveness.o const_fold.o diagnostics.o loops.o scev.o
obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o
obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o bpf_insn_array.o
@@ -27,6 +27,9 @@ obj-$(CONFIG_BPF_SYSCALL) += offload.o
obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o
obj-$(CONFIG_BPF_SYSCALL) += tcx.o
endif
+ifeq ($(CONFIG_KEYS),y)
+obj-$(CONFIG_BPF_SYSCALL) += keys.o
+endif
ifeq ($(CONFIG_PERF_EVENTS),y)
obj-$(CONFIG_BPF_SYSCALL) += stackmap.o
endif
@@ -39,6 +42,7 @@ obj-$(CONFIG_BPF_SYSCALL) += reuseport_array.o
endif
ifeq ($(CONFIG_SYSFS),y)
obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o
+obj-$(CONFIG_DEBUG_INFO_BTF_INLINE) += btf_vmlinux_inline.o
endif
ifeq ($(CONFIG_BPF_JIT),y)
obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
@@ -55,9 +59,7 @@ obj-$(CONFIG_BPF_SYSCALL) += cpumask.o
# semantics within pahole are revisited accordingly.
obj-${CONFIG_BPF_LSM} += bpf_lsm_proto.o bpf_lsm.o
endif
-ifneq ($(CONFIG_CRYPTO),)
-obj-$(CONFIG_BPF_SYSCALL) += crypto.o
-endif
+obj-$(CONFIG_BPF_CRYPTO) += crypto.o
obj-$(CONFIG_BPF_PRELOAD) += preload/
obj-$(CONFIG_BPF_SYSCALL) += relo_core.o
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 7b6847200b43..868589f07330 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -146,7 +146,7 @@ static long compute_pgoff(struct bpf_arena *arena, long uaddr)
struct apply_range_data {
struct bpf_arena *arena;
- struct page **pages;
+ struct llist_head *pages;
int i;
};
@@ -158,13 +158,17 @@ struct clear_range_data {
static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data)
{
struct apply_range_data *d = data;
+ struct llist_node *node;
struct page *page;
pte_t pteval;
if (!data)
return 0;
- page = d->pages[d->i];
+ node = READ_ONCE(d->pages->first);
+ if (WARN_ON_ONCE(!node))
+ return -EINVAL;
+ page = llist_entry(node, struct page, pcp_llist);
/* paranoia, similar to vmap_pages_pte_range() */
if (WARN_ON_ONCE(!pfn_valid(page_to_pfn(page))))
return -EINVAL;
@@ -197,6 +201,7 @@ static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data)
return -EBUSY;
set_pte_at(&init_mm, addr, pte, pteval);
#endif
+ WARN_ON_ONCE(llist_del_first(d->pages) != node);
d->i++;
WRITE_ONCE(d->arena->nr_pages, d->arena->nr_pages + 1);
return 0;
@@ -312,8 +317,8 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr)
INIT_WORK(&arena->free_work, arena_free_worker);
bpf_map_init_from_attr(&arena->map, attr);
- err = bpf_map_alloc_pages(&arena->map, NUMA_NO_NODE, 1, &arena->scratch_page);
- if (err)
+ arena->scratch_page = bpf_alloc_page(NUMA_NO_NODE, true);
+ if (!arena->scratch_page)
goto err_free_arena;
range_tree_init(&arena->rt);
@@ -481,7 +486,10 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
struct bpf_map *map = vmf->vma->vm_file->private_data;
struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
struct mem_cgroup *new_memcg, *old_memcg;
- struct page *page;
+ LLIST_HEAD(pages);
+ struct page *page, *new_page = NULL;
+ struct apply_range_data data;
+ vm_fault_t fault_ret;
long kbase, kaddr;
unsigned long flags;
int ret;
@@ -489,59 +497,123 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
kbase = bpf_arena_get_kern_vm_start(arena);
kaddr = kbase + (u32)(vmf->address);
- if (raw_res_spin_lock_irqsave(&arena->spinlock, flags))
+ page = vmalloc_to_page((void *)kaddr);
+ if (!page && !(arena->map.map_flags & BPF_F_SEGV_ON_FAULT)) {
+ /*
+ * Preallocate outside the lock so the allocation can reclaim;
+ * __GFP_RETRY_MAYFAIL keeps the OOM killer out of it.
+ */
+ bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
+ new_page = alloc_pages_node(map->numa_node,
+ GFP_KERNEL | __GFP_ZERO |
+ __GFP_ACCOUNT | __GFP_NOWARN |
+ __GFP_RETRY_MAYFAIL, 0);
+ bpf_map_memcg_exit(old_memcg, new_memcg);
+ }
+
+ if (raw_res_spin_lock_irqsave(&arena->spinlock, flags)) {
/*
* A failed lock means a possible deadlock was detected. Don't
* return VM_FAULT_RETRY: this handler never took mmap_lock, but
* the fault path would re-take it on retry and deadlock. Fail.
*/
+ if (new_page)
+ free_pages_nolock(new_page, 0);
return VM_FAULT_SIGBUS;
+ }
page = vmalloc_to_page((void *)kaddr);
if (page) {
- if (page == arena->scratch_page)
+ if (page == arena->scratch_page) {
/* BPF triggered scratch here; don't lazy-alloc over it */
- goto out_sigsegv;
+ fault_ret = (arena->map.map_flags & BPF_F_SEGV_ON_FAULT)
+ ? VM_FAULT_SIGSEGV : VM_FAULT_SIGBUS;
+ goto out_err_locked;
+ }
/* already have a page vmap-ed */
goto out;
}
+ if (arena->map.map_flags & BPF_F_SEGV_ON_FAULT) {
+ /*
+ * User space requested to segfault when page is not allocated
+ * by bpf prog
+ */
+ fault_ret = VM_FAULT_SIGSEGV;
+ goto out_err_locked;
+ }
+
+ if (is_range_tree_set(&arena->rt, vmf->pgoff, 1)) {
+ /* Allocated without a page: reserved or being freed */
+ fault_ret = VM_FAULT_SIGBUS;
+ goto out_err_locked;
+ }
+
bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
- if (arena->map.map_flags & BPF_F_SEGV_ON_FAULT)
- /* User space requested to segfault when page is not allocated by bpf prog */
- goto out_sigsegv_memcg;
+ if (!new_page) {
+ /*
+ * The probed page was freed meanwhile or preallocation failed;
+ * try the non-blocking allocator, we cannot sleep here.
+ */
+ new_page = bpf_alloc_page(map->numa_node, false);
+ if (!new_page) {
+ fault_ret = VM_FAULT_SIGBUS;
+ goto out_err_locked_memcg;
+ }
+ }
ret = range_tree_clear(&arena->rt, vmf->pgoff, 1);
- if (ret)
- goto out_sigsegv_memcg;
-
- struct apply_range_data data = { .arena = arena, .pages = &page, .i = 0 };
- /* Account into memcg of the process that created bpf_arena */
- ret = bpf_map_alloc_pages(map, NUMA_NO_NODE, 1, &page);
if (ret) {
- range_tree_set(&arena->rt, vmf->pgoff, 1);
- goto out_sigsegv_memcg;
+ fault_ret = VM_FAULT_SIGBUS;
+ goto out_err_locked_memcg;
}
+ llist_add(&new_page->pcp_llist, &pages);
+ data.arena = arena;
+ data.pages = &pages;
+ data.i = 0;
ret = apply_to_page_range(&init_mm, kaddr, PAGE_SIZE, apply_range_set_cb, &data);
if (ret) {
+ llist_del_first(&pages);
range_tree_set(&arena->rt, vmf->pgoff, 1);
- free_pages_nolock(page, 0);
- goto out_sigsegv_memcg;
+ fault_ret = VM_FAULT_SIGBUS;
+ goto out_err_locked_memcg;
}
flush_vmap_cache(kaddr, PAGE_SIZE);
bpf_map_memcg_exit(old_memcg, new_memcg);
+ /* new_page was consumed */
+ page = new_page;
+ new_page = NULL;
out:
page_ref_add(page, 1);
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
- vmf->page = page;
- return 0;
-out_sigsegv_memcg:
+ if (new_page)
+ free_pages_nolock(new_page, 0);
+
+ /*
+ * A free clears the kernel pte and then takes the page lock before it
+ * zaps user vma-s. Either it waits for this fault and zaps its pte, or
+ * the page is gone from here.
+ */
+ lock_page(page);
+ if (vmalloc_to_page((void *)kaddr) == page && !PageHWPoison(page)) {
+ vmf->page = page;
+ return VM_FAULT_LOCKED;
+ }
+ /* The page was freed meanwhile. Let the fault start over. */
+ fault_ret = PageHWPoison(page) ? VM_FAULT_HWPOISON : VM_FAULT_NOPAGE;
+ unlock_page(page);
+ put_page(page);
+ return fault_ret;
+
+out_err_locked_memcg:
bpf_map_memcg_exit(old_memcg, new_memcg);
-out_sigsegv:
+out_err_locked:
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
- return VM_FAULT_SIGSEGV;
+ if (new_page)
+ free_pages_nolock(new_page, 0);
+ return fault_ret;
}
static const struct vm_operations_struct arena_vm_ops = {
@@ -661,6 +733,27 @@ static u64 clear_lo32(u64 val)
return val & ~(u64)~0U;
}
+static int arena_adjust_tree(struct bpf_arena *arena, long uaddr, long page_cnt, long *pgoff)
+{
+ int ret;
+
+ /* Special case where user is requesting specific range. */
+ if (uaddr) {
+ ret = is_range_tree_set(&arena->rt, *pgoff, page_cnt);
+ if (ret)
+ return ret;
+ return range_tree_clear(&arena->rt, *pgoff, page_cnt);
+ }
+
+ ret = range_tree_find(&arena->rt, page_cnt);
+ if (ret < 0)
+ return ret;
+
+ *pgoff = ret;
+
+ return range_tree_clear(&arena->rt, *pgoff, page_cnt);
+}
+
/*
* Allocate pages and vmap them into kernel vmalloc area.
* Later the pages will be mmaped into user space vma.
@@ -673,13 +766,13 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt
u64 kern_vm_start = bpf_arena_get_kern_vm_start(arena);
struct mem_cgroup *new_memcg, *old_memcg;
struct apply_range_data data;
- struct page **pages = NULL;
- long remaining, mapped = 0;
- long alloc_pages;
+ LLIST_HEAD(pages);
+ long mapped = 0;
unsigned long flags;
long pgoff = 0;
u32 uaddr32;
- int ret, i;
+ long addr = 0;
+ int ret;
if (node_id != NUMA_NO_NODE &&
((unsigned int)node_id >= nr_node_ids || !node_online(node_id)))
@@ -696,89 +789,63 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt
/* requested address will be outside of user VMA */
return 0;
}
-
bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
- /* Cap allocation size to KMALLOC_MAX_CACHE_SIZE so kmalloc_nolock() can succeed. */
- alloc_pages = min(page_cnt, KMALLOC_MAX_CACHE_SIZE / sizeof(struct page *));
- pages = kmalloc_nolock(alloc_pages * sizeof(struct page *), __GFP_ACCOUNT, NUMA_NO_NODE);
- if (!pages) {
- bpf_map_memcg_exit(old_memcg, new_memcg);
- return 0;
- }
+
+ ret = bpf_alloc_pages(node_id, page_cnt, &pages, sleepable);
+ if (ret)
+ goto out_memcg;
+
data.arena = arena;
- data.pages = pages;
+ data.pages = &pages;
+ data.i = 0;
if (raw_res_spin_lock_irqsave(&arena->spinlock, flags))
goto out_free_pages;
- if (uaddr) {
- ret = is_range_tree_set(&arena->rt, pgoff, page_cnt);
- if (ret)
- goto out_unlock_free_pages;
- ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
- } else {
- ret = pgoff = range_tree_find(&arena->rt, page_cnt);
- if (pgoff >= 0)
- ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
+ ret = arena_adjust_tree(arena, uaddr, page_cnt, &pgoff);
+ if (ret) {
+ raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
+ goto out_free_pages;
}
- if (ret)
- goto out_unlock_free_pages;
- remaining = page_cnt;
uaddr32 = (u32)(arena->user_vm_start + pgoff * PAGE_SIZE);
- while (remaining) {
- long this_batch = min(remaining, alloc_pages);
-
- /* zeroing is needed, since alloc_pages_bulk() only fills in non-zero entries */
- memset(pages, 0, this_batch * sizeof(struct page *));
+ /*
+ * Earlier checks made sure that uaddr32 + page_cnt * PAGE_SIZE - 1
+ * will not overflow 32-bit. Lower 32-bit need to represent
+ * contiguous user address range.
+ * Map these pages at kern_vm_start base.
+ * kern_vm_start + uaddr32 + page_cnt * PAGE_SIZE - 1 can overflow
+ * lower 32-bit and it's ok.
+ */
+ ret = apply_to_page_range(&init_mm, kern_vm_start + uaddr32,
+ page_cnt << PAGE_SHIFT, apply_range_set_cb, &data);
+ mapped = data.i;
+ if (ret)
+ goto out_unmap;
- ret = bpf_map_alloc_pages(&arena->map, node_id, this_batch, pages);
- if (ret)
- goto out;
+ flush_vmap_cache(kern_vm_start + uaddr32, mapped << PAGE_SHIFT);
+ raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
- /*
- * Earlier checks made sure that uaddr32 + page_cnt * PAGE_SIZE - 1
- * will not overflow 32-bit. Lower 32-bit need to represent
- * contiguous user address range.
- * Map these pages at kern_vm_start base.
- * kern_vm_start + uaddr32 + page_cnt * PAGE_SIZE - 1 can overflow
- * lower 32-bit and it's ok.
- */
- data.i = 0;
- ret = apply_to_page_range(&init_mm,
- kern_vm_start + uaddr32 + (mapped << PAGE_SHIFT),
- this_batch << PAGE_SHIFT, apply_range_set_cb, &data);
- if (ret) {
- /* data.i pages were mapped, account them and free the remaining */
- mapped += data.i;
- for (i = data.i; i < this_batch; i++)
- free_pages_nolock(pages[i], 0);
- goto out;
- }
+ addr = clear_lo32(arena->user_vm_start) + uaddr32;
+ goto out_memcg;
- mapped += this_batch;
- remaining -= this_batch;
- }
+out_unmap:
+ /* Error handling: Undo partial mappings. */
flush_vmap_cache(kern_vm_start + uaddr32, mapped << PAGE_SHIFT);
- raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
- kfree_nolock(pages);
- bpf_map_memcg_exit(old_memcg, new_memcg);
- return clear_lo32(arena->user_vm_start) + uaddr32;
-out:
range_tree_set(&arena->rt, pgoff + mapped, page_cnt - mapped);
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
- if (mapped) {
- flush_vmap_cache(kern_vm_start + uaddr32, mapped << PAGE_SHIFT);
+ if (mapped)
arena_free_pages(arena, uaddr32, mapped, sleepable);
- }
- goto out_free_pages;
-out_unlock_free_pages:
- raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
+
out_free_pages:
- kfree_nolock(pages);
+ /* Error handling: Free back any unmapped pages. */
+ bpf_free_pages(&pages);
+
+out_memcg:
bpf_map_memcg_exit(old_memcg, new_memcg);
- return 0;
+
+ return addr;
}
/*
@@ -807,7 +874,7 @@ static void zap_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
* otherwise call B's mark would make call A skip a vml that A has
* not yet zapped for A's uaddr range.
*/
- mutex_lock(&arena->zap_mutex);
+ lockdep_assert_held(&arena->zap_mutex);
mutex_lock(&arena->lock);
my_gen = ++arena->zap_gen;
for (;;) {
@@ -841,49 +908,41 @@ static void zap_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
mutex_lock(&arena->lock);
}
mutex_unlock(&arena->lock);
- mutex_unlock(&arena->zap_mutex);
}
-static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, bool sleepable)
+/*
+ * The range stays allocated in the range tree until the pages are unmapped
+ * from vmalloc area and from all user space vma-s, so that nobody can
+ * populate it while stale TLB entries or user ptes are still around.
+ */
+static int arena_free_range(struct bpf_arena *arena, long uaddr, long page_cnt)
{
- struct mem_cgroup *new_memcg, *old_memcg;
- u64 full_uaddr, uaddr_end;
- long kaddr, pgoff;
- struct page *page;
- struct llist_head free_pages;
- struct llist_node *pos, *t;
- struct arena_free_span *s;
+ u64 full_uaddr = clear_lo32(arena->user_vm_start) + uaddr;
+ long kaddr = bpf_arena_get_kern_vm_start(arena) + uaddr;
+ long pgoff = compute_pgoff(arena, uaddr);
struct clear_range_data cdata;
+ struct llist_node *pos, *t;
+ LLIST_HEAD(free_pages);
unsigned long flags;
- int ret = 0;
-
- /* only aligned lower 32-bit are relevant */
- uaddr = (u32)uaddr;
- uaddr &= PAGE_MASK;
- kaddr = bpf_arena_get_kern_vm_start(arena) + uaddr;
- full_uaddr = clear_lo32(arena->user_vm_start) + uaddr;
- if (full_uaddr < arena->user_vm_start)
- return;
- uaddr_end = min(arena->user_vm_end, full_uaddr + (page_cnt << PAGE_SHIFT));
- if (full_uaddr >= uaddr_end)
- return;
-
- page_cnt = (uaddr_end - full_uaddr) >> PAGE_SHIFT;
- pgoff = compute_pgoff(arena, uaddr);
- bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
+ struct page *page;
+ int ret;
- if (!sleepable)
- goto defer;
+ /* An overlapping free must not release the range before this one is done */
+ guard(mutex)(&arena->zap_mutex);
ret = raw_res_spin_lock_irqsave(&arena->spinlock, flags);
-
- /* Can't proceed without holding the spinlock so defer the free */
if (ret)
- goto defer;
+ return ret;
- range_tree_set(&arena->rt, pgoff, page_cnt);
+ /*
+ * Take the free pages of the range too. It allocates, and can fail,
+ * only when the whole range is free. Then there is nothing to do.
+ */
+ if (range_tree_clear(&arena->rt, pgoff, page_cnt)) {
+ raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
+ return 0;
+ }
- init_llist_head(&free_pages);
cdata.arena = arena;
cdata.free_pages = &free_pages;
/* clear ptes and collect struct pages */
@@ -893,6 +952,12 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
/* drop the lock to do the tlb flush and zap pages */
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
+ /* wait for arena_vm_fault() that found the page before its pte was cleared */
+ llist_for_each_entry(page, free_pages.first, pcp_llist) {
+ lock_page(page);
+ unlock_page(page);
+ }
+
/* ensure no stale TLB entries */
flush_tlb_kernel_range(kaddr, kaddr + (page_cnt * PAGE_SIZE));
@@ -911,8 +976,39 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
zap_pages(arena, full_uaddr, 1);
__free_page(page);
}
- bpf_map_memcg_exit(old_memcg, new_memcg);
+ /* The callers hold no other rqspinlock, so this one can only time out */
+ while (raw_res_spin_lock_irqsave(&arena->spinlock, flags))
+ cond_resched();
+ range_tree_set(&arena->rt, pgoff, page_cnt);
+ raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
+ return 0;
+}
+
+static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, bool sleepable)
+{
+ struct mem_cgroup *new_memcg, *old_memcg;
+ u64 full_uaddr, uaddr_end;
+ struct arena_free_span *s;
+
+ /* 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)
+ return;
+ uaddr_end = min(arena->user_vm_end, full_uaddr + (page_cnt << PAGE_SHIFT));
+ if (full_uaddr >= uaddr_end)
+ return;
+
+ page_cnt = (uaddr_end - full_uaddr) >> PAGE_SHIFT;
+ bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
+
+ /* Can't proceed without holding the spinlock so defer the free */
+ if (!sleepable || arena_free_range(arena, uaddr, page_cnt))
+ goto defer;
+
+ bpf_map_memcg_exit(old_memcg, new_memcg);
return;
defer:
@@ -974,66 +1070,18 @@ static void arena_free_worker(struct work_struct *work)
{
struct bpf_arena *arena = container_of(work, struct bpf_arena, free_work);
struct mem_cgroup *new_memcg, *old_memcg;
- struct llist_node *list, *pos, *t;
+ struct llist_node *pos, *t;
struct arena_free_span *s;
- u64 arena_vm_start, user_vm_start;
- struct llist_head free_pages;
- struct clear_range_data cdata;
- struct page *page;
- unsigned long full_uaddr;
- long kaddr, page_cnt, pgoff;
- unsigned long flags;
-
- if (raw_res_spin_lock_irqsave(&arena->spinlock, flags)) {
- schedule_work(work);
- return;
- }
bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
- init_llist_head(&free_pages);
- cdata.arena = arena;
- cdata.free_pages = &free_pages;
- arena_vm_start = bpf_arena_get_kern_vm_start(arena);
- user_vm_start = bpf_arena_get_user_vm_start(arena);
-
- list = llist_del_all(&arena->free_spans);
- llist_for_each(pos, list) {
+ llist_for_each_safe(pos, t, llist_del_all(&arena->free_spans)) {
s = llist_entry(pos, struct arena_free_span, node);
- page_cnt = s->page_cnt;
- kaddr = arena_vm_start + s->uaddr;
- pgoff = compute_pgoff(arena, s->uaddr);
-
- /* clear ptes and collect pages in free_pages llist */
- apply_to_existing_page_range(&init_mm, kaddr, page_cnt << PAGE_SHIFT,
- apply_range_clear_cb, &cdata);
-
- range_tree_set(&arena->rt, pgoff, page_cnt);
- }
- raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
-
- /* Iterate the list again without holding spinlock to do the tlb flush and zap_pages */
- llist_for_each_safe(pos, t, list) {
- s = llist_entry(pos, struct arena_free_span, node);
- page_cnt = s->page_cnt;
- full_uaddr = clear_lo32(user_vm_start) + s->uaddr;
- kaddr = arena_vm_start + s->uaddr;
-
- /* ensure no stale TLB entries */
- flush_tlb_kernel_range(kaddr, kaddr + (page_cnt * PAGE_SIZE));
-
- /* remove pages from user vmas */
- zap_pages(arena, full_uaddr, page_cnt);
-
+ while (arena_free_range(arena, s->uaddr, s->page_cnt))
+ cond_resched();
kfree_nolock(s);
}
- /* free all pages collected by apply_to_existing_page_range() in the first loop */
- llist_for_each_safe(pos, t, __llist_del_all(&free_pages)) {
- page = llist_entry(pos, struct page, pcp_llist);
- __free_page(page);
- }
-
bpf_map_memcg_exit(old_memcg, new_memcg);
}
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 0ce26b538075..133f189bf6d2 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -438,15 +438,15 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
u32 size;
int cpu, off = 0;
- if (unlikely((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS))
- /* unknown flags */
+ if (unlikely(((map_flags & BPF_EXIST_FLAGS) == BPF_EXIST_FLAGS) ||
+ ((u32)map_flags & ~(BPF_EXIST_FLAGS | BPF_CPU_FLAGS))))
return -EINVAL;
if (unlikely(index >= array->map.max_entries))
/* all elements were pre-allocated, cannot insert a new one */
return -E2BIG;
- if (unlikely(map_flags == BPF_NOEXIST))
+ if (unlikely(map_flags & BPF_NOEXIST))
/* all elements already exist */
return -EEXIST;
@@ -1035,7 +1035,7 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
}
mutex_lock(&prog->aux->ext_mutex);
- is_extended = prog->aux->is_extended;
+ is_extended = prog->aux->freplace_link_cnt > 0;
if (!is_extended)
prog->aux->prog_array_member_cnt++;
mutex_unlock(&prog->aux->ext_mutex);
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c
index 47282ffeeaf9..43d5740034c0 100644
--- a/kernel/bpf/backtrack.c
+++ b/kernel/bpf/backtrack.c
@@ -9,7 +9,8 @@
/* for any branch, call, exit record the history of jmps in the given state */
int bpf_push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur,
- int insn_flags, int spi, int frame, u64 linked_regs)
+ int insn_flags, int spi, int frame, const u16 *linked_regs,
+ u8 linked_regs_cnt)
{
u32 cnt = cur->jmp_history_cnt;
struct bpf_jmp_history_entry *p;
@@ -27,10 +28,13 @@ int bpf_push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state
env->cur_hist_ent->flags |= insn_flags;
env->cur_hist_ent->spi = spi;
env->cur_hist_ent->frame = frame;
- verifier_bug_if(env->cur_hist_ent->linked_regs != 0, env,
- "insn history: insn_idx %d linked_regs: %#llx",
- env->insn_idx, env->cur_hist_ent->linked_regs);
- env->cur_hist_ent->linked_regs = linked_regs;
+ verifier_bug_if(env->cur_hist_ent->linked_regs_cnt != 0, env,
+ "insn history: insn_idx %d has %u linked regs",
+ env->insn_idx, env->cur_hist_ent->linked_regs_cnt);
+ if (linked_regs_cnt)
+ memcpy(env->cur_hist_ent->linked_regs, linked_regs,
+ linked_regs_cnt * sizeof(*linked_regs));
+ env->cur_hist_ent->linked_regs_cnt = linked_regs_cnt;
return 0;
}
@@ -47,7 +51,9 @@ int bpf_push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state
p->flags = insn_flags;
p->spi = spi;
p->frame = frame;
- p->linked_regs = linked_regs;
+ if (linked_regs_cnt)
+ memcpy(p->linked_regs, linked_regs, linked_regs_cnt * sizeof(*linked_regs));
+ p->linked_regs_cnt = linked_regs_cnt;
cur->jmp_history_cnt = cnt;
env->cur_hist_ent = p;
@@ -123,13 +129,26 @@ static inline void bt_reset(struct backtrack_state *bt)
bt->env = env;
}
-static inline u32 bt_empty(struct backtrack_state *bt)
+static inline bool bt_frame_stack_empty(struct backtrack_state *bt, u32 frame)
{
- u64 mask = 0;
+ return bitmap_empty(bt->stack_masks[frame], MAX_BPF_STACK_SLOTS);
+}
+
+static inline bool bt_stack_empty(struct backtrack_state *bt)
+{
+ return bt_frame_stack_empty(bt, bt->frame);
+}
+
+static inline bool bt_empty(struct backtrack_state *bt)
+{
+ u32 mask = 0;
int i;
- for (i = 0; i <= bt->frame; i++)
- mask |= bt->reg_masks[i] | bt->stack_masks[i] | bt->stack_arg_masks[i];
+ for (i = 0; i <= bt->frame; i++) {
+ mask |= bt->reg_masks[i] | bt->stack_arg_masks[i];
+ if (!bt_frame_stack_empty(bt, i))
+ return false;
+ }
return mask == 0;
}
@@ -181,7 +200,7 @@ static inline void bt_clear_reg(struct backtrack_state *bt, u32 reg)
static inline void bt_clear_frame_slot(struct backtrack_state *bt, u32 frame, u32 slot)
{
- bt->stack_masks[frame] &= ~(1ull << slot);
+ __clear_bit(slot, bt->stack_masks[frame]);
}
static inline u32 bt_frame_reg_mask(struct backtrack_state *bt, u32 frame)
@@ -194,14 +213,14 @@ static inline u32 bt_reg_mask(struct backtrack_state *bt)
return bt->reg_masks[bt->frame];
}
-static inline u64 bt_frame_stack_mask(struct backtrack_state *bt, u32 frame)
+static inline unsigned long *bt_frame_stack_mask(struct backtrack_state *bt, u32 frame)
{
return bt->stack_masks[frame];
}
-static inline u64 bt_stack_mask(struct backtrack_state *bt)
+static inline unsigned long *bt_stack_mask(struct backtrack_state *bt)
{
- return bt->stack_masks[bt->frame];
+ return bt_frame_stack_mask(bt, bt->frame);
}
static inline u8 bt_stack_arg_mask(struct backtrack_state *bt)
@@ -233,17 +252,16 @@ static void fmt_reg_mask(char *buf, ssize_t buf_sz, u32 reg_mask)
break;
}
}
-/* format stack slots bitmask, e.g., "-8,-24,-40" for 0x15 mask */
-void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask)
+
+/* format stack slots bitmask, e.g., "-8,-24,-40" for slots 0, 2 and 4 */
+void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, const unsigned long *stack_mask)
{
- DECLARE_BITMAP(mask, 64);
bool first = true;
int i, n;
buf[0] = '\0';
- bitmap_from_u64(mask, stack_mask);
- for_each_set_bit(i, mask, 64) {
+ for_each_set_bit(i, stack_mask, MAX_BPF_STACK_SLOTS) {
n = snprintf(buf, buf_sz, "%s%d", first ? "" : ",", -(i + 1) * 8);
first = false;
buf += n;
@@ -406,15 +424,18 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
if (class == BPF_STX)
bt_set_reg(bt, sreg);
} else if (class == BPF_JMP || class == BPF_JMP32) {
- if (bpf_pseudo_call(insn)) {
- int subprog_insn_idx, subprog;
+ if (bpf_pseudo_call(insn) || bpf_is_callx(insn)) {
+ int subprog_insn_idx, subprog = -1;
- subprog_insn_idx = idx + insn->imm + 1;
- subprog = bpf_find_subprog(env, subprog_insn_idx);
- if (subprog < 0)
- return -EFAULT;
+ if (bpf_pseudo_call(insn)) {
+ subprog_insn_idx = idx + insn->imm + 1;
+ subprog = bpf_find_subprog(env, subprog_insn_idx);
+ if (subprog < 0)
+ return -EFAULT;
+ }
- if (bpf_subprog_is_global(env, subprog)) {
+ /* callx calls static subprogs only */
+ if (subprog >= 0 && bpf_subprog_is_global(env, subprog)) {
/* check that jump history doesn't have any
* extra instructions from subprog; the next
* instruction after call to global subprog
@@ -423,6 +444,10 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
*/
verifier_bug_if(idx + 1 != subseq_idx, env,
"extra insn from subprog");
+ /* global subprog always sets R0 */
+ bt_clear_reg(bt, BPF_REG_0);
+ /* and if it does not set R2, main pass would catch it */
+ bt_clear_reg(bt, BPF_REG_2);
/* r1-r5 are invalidated after subprog call,
* so for global func call it shouldn't be set
* anymore
@@ -432,8 +457,6 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
bt_reg_mask(bt));
return -EFAULT;
}
- /* global subprog always sets R0 */
- bt_clear_reg(bt, BPF_REG_0);
return 0;
} else {
/* static subprog call instruction, which
@@ -450,10 +473,11 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
/* we are now tracking register spills correctly,
* so any instance of leftover slots is a bug
*/
- if (bt_stack_mask(bt) != 0) {
- verifier_bug(env,
- "static subprog leftover stack slots %llx",
- bt_stack_mask(bt));
+ if (!bt_stack_empty(bt)) {
+ bpf_fmt_stack_mask(env->tmp_str_buf, TMP_STR_BUF_LEN,
+ bt_stack_mask(bt));
+ verifier_bug(env, "static subprog leftover stack slots %s",
+ env->tmp_str_buf);
return -EFAULT;
}
/* propagate r1-r5 to the caller */
@@ -486,9 +510,11 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
bt_reg_mask(bt));
return -EFAULT;
}
- if (bt_stack_mask(bt) != 0) {
- verifier_bug(env, "callback leftover stack slots %llx",
- bt_stack_mask(bt));
+ if (!bt_stack_empty(bt)) {
+ bpf_fmt_stack_mask(env->tmp_str_buf, TMP_STR_BUF_LEN,
+ bt_stack_mask(bt));
+ verifier_bug(env, "callback leftover stack slots %s",
+ env->tmp_str_buf);
return -EFAULT;
}
/* clear r1-r5 in callback subprog's mask */
@@ -506,6 +532,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
return -ENOTSUPP;
/* regular helper call sets R0 */
bt_clear_reg(bt, BPF_REG_0);
+ /* kfunc might also set R2 */
+ bt_clear_reg(bt, BPF_REG_2);
if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
/* if backtracking was looking for registers R1-R5
* they should have been found already.
@@ -520,40 +548,51 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
return -EFAULT;
}
} else if (opcode == BPF_EXIT) {
- bool from_subprog_call, r0_precise;
+ bool from_subprog_call, r0_precise, r2_precise;
/* BPF_EXIT in subprog or callback always returns
* right after the call instruction, so by checking
* whether the instruction at subseq_idx-1 is subprog
* call or not we can distinguish actual exit from
* *subprog* from exit from *callback*. In the former
- * case, we need to propagate r0 precision, if
- * necessary. In the former we never do that.
+ * case, we need to propagate the precision of the
+ * return registers, if necessary. In the latter we
+ * never do that.
*/
from_subprog_call = subseq_idx - 1 >= 0 &&
- bpf_pseudo_call(&env->prog->insnsi[subseq_idx - 1]);
+ (bpf_pseudo_call(&env->prog->insnsi[subseq_idx - 1]) ||
+ bpf_is_callx(&env->prog->insnsi[subseq_idx - 1]));
r0_precise = from_subprog_call && bt_is_reg_set(bt, BPF_REG_0);
+ r2_precise = from_subprog_call && bt_is_reg_set(bt, BPF_REG_2);
/* Backtracking to a nested function call, 'idx' is a part of
* the inner frame 'subseq_idx' is a part of the outer frame.
- * In case of a regular function call, instructions giving
- * precision to registers R1-R5 should have been found already.
+ * In case of a regular function call, the callee defines the
+ * return registers R0 and R2, so clear them before checking
+ * that instructions giving precision to registers R1-R5 have
+ * been found already. R2 is also an argument register, hence
+ * it has to be cleared before that check.
* In case of a callback from bpf_loop(), R{1,4} in the calling
- * frame would be set as precise and that is correct.
+ * frame would be set as precise and that is correct, and R2
+ * might be a precise helper argument as well, so leave it set.
*/
+ bt_clear_reg(bt, BPF_REG_0);
+ if (from_subprog_call)
+ bt_clear_reg(bt, BPF_REG_2);
if (from_subprog_call && (bt_reg_mask(bt) & BPF_REGMASK_ARGS)) {
verifier_bug(env, "backtracking exit unexpected regs %x",
bt_reg_mask(bt));
return -EFAULT;
}
- bt_clear_reg(bt, BPF_REG_0);
if (bt_subprog_enter(bt))
return -EFAULT;
if (r0_precise)
bt_set_reg(bt, BPF_REG_0);
+ if (r2_precise)
+ bt_set_reg(bt, BPF_REG_2);
/* r6-r9 and stack slots will stay set in caller frame
* bitmasks until we return back from callee(s)
*/
@@ -693,10 +732,12 @@ void bpf_mark_all_scalars_precise(struct bpf_verifier_env *env,
i, j);
}
}
- for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) {
- if (!bpf_is_spilled_reg(&func->stack[j]))
+ for (j = 0; j < bpf_stack_nr_slots(func); j++) {
+ struct bpf_stack_state *ss = bpf_stack_slot(func, j);
+
+ if (!bpf_is_spilled_reg(ss))
continue;
- reg = &func->stack[j].spilled_ptr;
+ reg = &ss->spilled_ptr;
if (reg->type != SCALAR_VALUE || reg->precise)
continue;
reg->precise = true;
@@ -808,6 +849,7 @@ int bpf_mark_chain_precision(struct bpf_verifier_env *env,
int subseq_idx = -1;
struct bpf_func_state *func;
bool tmp, skip_first = true;
+ struct bpf_stack_state *ss;
struct bpf_reg_state *reg;
int i, fr, err;
@@ -846,6 +888,10 @@ int bpf_mark_chain_precision(struct bpf_verifier_env *env,
}
if (last_idx < 0) {
+ /* Consecutive checkpoints can have no instructions between them. */
+ if (st->parent)
+ goto parent;
+
/* we are at the entry into subprog, which
* is expected for global funcs, but only if
* requested precise registers are R1-R5
@@ -854,7 +900,7 @@ int bpf_mark_chain_precision(struct bpf_verifier_env *env,
if (st->curframe == 0 &&
st->frame[0]->subprogno > 0 &&
st->frame[0]->callsite == BPF_MAIN_FUNC &&
- bt_stack_mask(bt) == 0 &&
+ bt_stack_empty(bt) &&
(bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) == 0) {
bitmap_from_u64(mask, bt_reg_mask(bt));
for_each_set_bit(i, mask, 32) {
@@ -868,8 +914,9 @@ int bpf_mark_chain_precision(struct bpf_verifier_env *env,
return 0;
}
- verifier_bug(env, "backtracking func entry subprog %d reg_mask %x stack_mask %llx",
- st->frame[0]->subprogno, bt_reg_mask(bt), bt_stack_mask(bt));
+ bpf_fmt_stack_mask(env->tmp_str_buf, TMP_STR_BUF_LEN, bt_stack_mask(bt));
+ verifier_bug(env, "backtracking func entry subprog %d reg_mask %x stack_mask %s",
+ st->frame[0]->subprogno, bt_reg_mask(bt), env->tmp_str_buf);
return -EFAULT;
}
@@ -909,6 +956,7 @@ int bpf_mark_chain_precision(struct bpf_verifier_env *env,
return -EFAULT;
}
}
+parent:
st = st->parent;
if (!st)
break;
@@ -930,18 +978,18 @@ int bpf_mark_chain_precision(struct bpf_verifier_env *env,
}
}
- bitmap_from_u64(mask, bt_frame_stack_mask(bt, fr));
- for_each_set_bit(i, mask, 64) {
- if (verifier_bug_if(i >= func->allocated_stack / BPF_REG_SIZE,
+ for_each_set_bit(i, bt_frame_stack_mask(bt, fr), MAX_BPF_STACK_SLOTS) {
+ if (verifier_bug_if(i >= bpf_stack_nr_slots(func),
env, "stack slot %d, total slots %d",
- i, func->allocated_stack / BPF_REG_SIZE))
+ i, bpf_stack_nr_slots(func)))
return -EFAULT;
- if (!bpf_is_spilled_scalar_reg(&func->stack[i])) {
+ ss = bpf_stack_slot(func, i);
+ if (!bpf_is_spilled_scalar_reg(ss)) {
bt_clear_frame_slot(bt, fr, i);
continue;
}
- reg = &func->stack[i].spilled_ptr;
+ reg = &ss->spilled_ptr;
if (reg->precise) {
bt_clear_frame_slot(bt, fr, i);
} else {
diff --git a/kernel/bpf/bpf_insn_array.c b/kernel/bpf/bpf_insn_array.c
index a2f84afe6f7c..92bbb71576ae 100644
--- a/kernel/bpf/bpf_insn_array.c
+++ b/kernel/bpf/bpf_insn_array.c
@@ -199,12 +199,16 @@ int bpf_insn_array_init(struct bpf_map *map, const struct bpf_prog *prog)
return -EBUSY;
/*
- * Reset all the map indexes to the original values. This is needed,
- * e.g., when a replay of verification with different log level should
- * be performed.
+ * Reset the map to its pre-verification state. The xlated and jitted
+ * offsets and the jitted target pointers are recomputed by the verifier
+ * and the JIT for this program, so any values left by a previous owner
+ * must be cleared here.
*/
- for (i = 0; i < map->max_entries; i++)
+ for (i = 0; i < map->max_entries; i++) {
values[i].xlated_off = values[i].orig_off;
+ values[i].jitted_off = 0;
+ insn_array->ips[i] = 0;
+ }
return 0;
}
@@ -228,10 +232,11 @@ void bpf_insn_array_release(struct bpf_map *map)
{
struct bpf_insn_array *insn_array = cast_insn_array(map);
- atomic_set(&insn_array->used, 0);
+ /* Paired with atomic_xchg() in bpf_insn_array_init(). */
+ atomic_set_release(&insn_array->used, 0);
}
-void bpf_insn_array_adjust(struct bpf_map *map, u32 off, u32 len)
+void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len)
{
struct bpf_insn_array *insn_array = cast_insn_array(map);
int i;
@@ -240,7 +245,7 @@ void bpf_insn_array_adjust(struct bpf_map *map, u32 off, u32 len)
return;
for (i = 0; i < map->max_entries; i++) {
- if (insn_array->values[i].xlated_off <= off)
+ if (insn_array->values[i].xlated_off < first)
continue;
if (insn_array->values[i].xlated_off == INSN_DELETED)
continue;
diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c
index d9191df5de22..f0b29a170935 100644
--- a/kernel/bpf/bpf_iter.c
+++ b/kernel/bpf/bpf_iter.c
@@ -72,6 +72,15 @@ static inline bool bpf_iter_target_support_resched(const struct bpf_iter_target_
return tinfo->reg_info->feature & BPF_ITER_RESCHED;
}
+/* Only allow sleepable program for resched-able iterator that can run one */
+static bool bpf_iter_target_support_prog(const struct bpf_iter_target_info *tinfo,
+ const struct bpf_prog *prog)
+{
+ return !prog->sleepable ||
+ (bpf_iter_target_support_resched(tinfo) &&
+ !(tinfo->reg_info->feature & BPF_ITER_NOSLEEP));
+}
+
static bool bpf_iter_support_resched(struct seq_file *seq)
{
struct bpf_iter_priv_data *iter_priv;
@@ -342,6 +351,13 @@ int bpf_iter_prog_supported(struct bpf_prog *prog)
const char *prefix = BPF_ITER_FUNC_PREFIX;
int prefix_len = strlen(prefix);
+ /*
+ * The attach function of a program that has a target program is a
+ * function of that program, whatever its name is.
+ */
+ if (prog->aux->dst_prog)
+ return -EINVAL;
+
if (strncmp(attach_fname, prefix, prefix_len))
return -EINVAL;
@@ -409,6 +425,8 @@ static int bpf_iter_link_replace(struct bpf_link *link,
struct bpf_prog *new_prog,
struct bpf_prog *old_prog)
{
+ struct bpf_iter_link *iter_link =
+ container_of(link, struct bpf_iter_link, link);
int ret = 0;
mutex_lock(&link_mutex);
@@ -419,11 +437,19 @@ static int bpf_iter_link_replace(struct bpf_link *link,
if (link->prog->type != new_prog->type ||
link->prog->expected_attach_type != new_prog->expected_attach_type ||
- link->prog->aux->attach_btf_id != new_prog->aux->attach_btf_id) {
+ link->prog->aux->attach_btf_id != new_prog->aux->attach_btf_id ||
+ !bpf_iter_target_support_prog(iter_link->tinfo, new_prog)) {
ret = -EINVAL;
goto out_unlock;
}
+ if (iter_link->tinfo->reg_info->check_prog) {
+ ret = iter_link->tinfo->reg_info->check_prog(new_prog,
+ &iter_link->aux);
+ if (ret)
+ goto out_unlock;
+ }
+
old_prog = xchg(&link->prog, new_prog);
bpf_prog_put(old_prog);
@@ -544,8 +570,7 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
if (!tinfo)
return -ENOENT;
- /* Only allow sleepable program for resched-able iterator */
- if (prog->sleepable && !bpf_iter_target_support_resched(tinfo))
+ if (!bpf_iter_target_support_prog(tinfo, prog))
return -EINVAL;
link = kzalloc_obj(*link, GFP_USER | __GFP_NOWARN);
diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c
index 6fc6a4b672b5..b12fcec47ffa 100644
--- a/kernel/bpf/bpf_local_storage.c
+++ b/kernel/bpf/bpf_local_storage.c
@@ -199,7 +199,7 @@ static void bpf_selem_unlink_storage_nolock_misc(struct bpf_local_storage_elem *
}
if (pin_owner)
- refcount_dec(&local_storage->owner_refcnt);
+ bpf_local_storage_unpin_owner(local_storage);
}
/* local_storage->lock must be held and selem->local_storage == local_storage.
@@ -240,24 +240,26 @@ void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage,
hlist_add_head_rcu(&selem->snode, &local_storage->list);
}
+/* Must be called with the owning local_storage->lock held. */
static int bpf_selem_unlink_map(struct bpf_local_storage_elem *selem)
{
struct bpf_local_storage *local_storage;
struct bpf_local_storage_map *smap;
struct bpf_local_storage_map_bucket *b;
- unsigned long flags;
int err;
+ lockdep_assert_irqs_disabled();
+
local_storage = rcu_dereference_check(selem->local_storage,
bpf_rcu_lock_held());
smap = rcu_dereference_check(SDATA(selem)->smap, bpf_rcu_lock_held());
b = select_bucket(smap, local_storage);
- err = raw_res_spin_lock_irqsave(&b->lock, flags);
+ err = raw_res_spin_lock(&b->lock);
if (err)
return err;
hlist_del_init_rcu(&selem->map_node);
- raw_res_spin_unlock_irqrestore(&b->lock, flags);
+ raw_res_spin_unlock(&b->lock);
return 0;
}
@@ -350,7 +352,8 @@ out:
static void bpf_selem_unlink_nofail(struct bpf_local_storage_elem *selem,
struct bpf_local_storage_map_bucket *b)
{
- bool in_map_free = !!b, free_storage = false;
+ bool in_map_free = !!b, free_storage = false, sleepable_users = false;
+ bool keep_fields = false;
struct bpf_local_storage *local_storage;
struct bpf_local_storage_map *smap;
unsigned long flags;
@@ -360,17 +363,26 @@ static void bpf_selem_unlink_nofail(struct bpf_local_storage_elem *selem,
smap = rcu_dereference_check(SDATA(selem)->smap, bpf_rcu_lock_held());
if (smap) {
+ /* A map that is freed, or NULL here, has no program left. */
+ sleepable_users = !in_map_free &&
+ (atomic64_read(&smap->map.sleepable_refcnt) ||
+ READ_ONCE(smap->map.was_inner_map));
b = b ? : select_bucket(smap, local_storage);
err = raw_res_spin_lock_irqsave(&b->lock, flags);
if (!err) {
/*
* Call bpf_obj_free_fields() under b->lock to make sure it is done
* exactly once for an selem. Safe to free special fields immediately
- * as no BPF program should be referencing the selem.
+ * as no BPF program should be referencing the selem, unless it
+ * is sleepable: see below.
*/
if (likely(selem_linked_to_map(selem))) {
hlist_del_init_rcu(&selem->map_node);
- bpf_obj_free_fields(smap->map.record, SDATA(selem)->data);
+ if (sleepable_users)
+ keep_fields = true;
+ else
+ bpf_obj_free_fields(smap->map.record,
+ SDATA(selem)->data);
unlink++;
}
raw_res_spin_unlock_irqrestore(&b->lock, flags);
@@ -385,7 +397,15 @@ static void bpf_selem_unlink_nofail(struct bpf_local_storage_elem *selem,
* with selem1 as raw_res_spin_lock_irqsave() returns -ETIMEDOUT.
*/
WARN_ON_ONCE(err && in_map_free);
- if (!err || in_map_free)
+ /*
+ * A sleepable program can still use, and fill, the fields of a value
+ * that it got before it let go of the owner. As for bpf_selem_unlink(),
+ * bpf_selem_free_trace_rcu() frees them, and takes smap for that:
+ * map_free() waits for this RCU read side critical section and then
+ * for the callback. It must not take smap away from an selem that
+ * destroy() has unlinked from the map.
+ */
+ if (in_map_free ? err || unlink : !err && !keep_fields)
RCU_INIT_POINTER(SDATA(selem)->smap, NULL);
}
@@ -429,13 +449,18 @@ static void bpf_selem_unlink_nofail(struct bpf_local_storage_elem *selem,
* then freed after an RCU grace period. However, if destroy() and map_free() are
* racing or rqspinlock returns errors in unlikely situations (unlink != 2), free
* the selem only after both map_free() and destroy() see the selem.
+ *
+ * The owner that map_free() takes an selem from is alive: sleepable code, which
+ * is in no RCU read side critical section, may be walking its list for another
+ * map. And it can still use a value that it got before it let go of an owner
+ * that destroy() is called for.
*/
if (unlink == 2 ||
atomic_cmpxchg(&selem->state, SELEM_UNLINKED, SELEM_TOFREE) == SELEM_UNLINKED)
- bpf_selem_free(selem, true);
+ bpf_selem_free(selem, !in_map_free && !sleepable_users);
if (free_storage)
- bpf_local_storage_free(local_storage, true);
+ bpf_local_storage_free(local_storage, !in_map_free);
}
void __bpf_local_storage_insert_cache(struct bpf_local_storage *local_storage,
@@ -507,8 +532,6 @@ int bpf_local_storage_alloc(void *owner,
if (err)
goto uncharge;
- bpf_selem_link_map_nolock(b, first_selem);
-
owner_storage_ptr =
(struct bpf_local_storage **)owner_storage(smap, owner);
/* Publish storage to the owner.
@@ -523,11 +546,17 @@ int bpf_local_storage_alloc(void *owner,
*/
prev_storage = cmpxchg(owner_storage_ptr, NULL, storage);
if (unlikely(prev_storage)) {
- bpf_selem_unlink_map_nolock(first_selem);
raw_res_spin_unlock_irqrestore(&b->lock, flags);
err = -EAGAIN;
goto uncharge;
}
+ /*
+ * Not before the storage is published: the owner does not wait for
+ * users of a storage that it knows nothing of, so that nobody may find
+ * such a storage through the map. b->lock holds off those who find
+ * first_selem through the owner and want to unlink it.
+ */
+ bpf_selem_link_map_nolock(b, first_selem);
raw_res_spin_unlock_irqrestore(&b->lock, flags);
return 0;
@@ -552,7 +581,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
struct bpf_local_storage *local_storage;
struct bpf_local_storage_map_bucket *b;
HLIST_HEAD(old_selem_free_list);
- unsigned long flags, b_flags;
+ unsigned long flags;
int err;
/* BPF_EXIST and BPF_NOEXIST cannot be both set */
@@ -637,7 +666,8 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
b = select_bucket(smap, local_storage);
- err = raw_res_spin_lock_irqsave(&b->lock, b_flags);
+ /* local_storage->lock is held, so IRQs are already disabled. */
+ err = raw_res_spin_lock(&b->lock);
if (err)
goto unlock;
@@ -655,7 +685,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
&old_selem_free_list);
}
- raw_res_spin_unlock_irqrestore(&b->lock, b_flags);
+ raw_res_spin_unlock(&b->lock);
unlock:
raw_res_spin_unlock_irqrestore(&local_storage->lock, flags);
free_selem:
@@ -750,8 +780,8 @@ u32 bpf_local_storage_destroy(struct bpf_local_storage *local_storage)
while (refcount_read(&local_storage->owner_refcnt))
cpu_relax();
/*
- * Paired with refcount_dec() in bpf_selem_unlink_nofail()
- * to make sure destroy() sees the correct local_storage->mem_charge.
+ * Paired with bpf_local_storage_unpin_owner() to make sure
+ * destroy() sees the correct local_storage->mem_charge.
*/
smp_mb();
}
diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c
index 5ed7cb4b98c0..3858886b0232 100644
--- a/kernel/bpf/bpf_lru_list.c
+++ b/kernel/bpf/bpf_lru_list.c
@@ -71,7 +71,7 @@ static void __bpf_lru_node_move_in(struct bpf_lru_list *l,
return;
bpf_lru_list_count_inc(l, tgt_type);
- node->type = tgt_type;
+ WRITE_ONCE(node->type, tgt_type);
bpf_lru_node_clear_ref(node);
/* Reset pending_free only when moving to the free list */
if (tgt_type == BPF_LRU_LIST_T_FREE)
@@ -94,7 +94,7 @@ static void __bpf_lru_node_move(struct bpf_lru_list *l,
if (node->type != tgt_type) {
bpf_lru_list_count_dec(l, node->type);
bpf_lru_list_count_inc(l, tgt_type);
- node->type = tgt_type;
+ WRITE_ONCE(node->type, tgt_type);
}
bpf_lru_node_clear_ref(node);
@@ -298,9 +298,6 @@ static void bpf_lru_list_push_free(struct bpf_lru_list *l,
{
unsigned long flags;
- if (WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(node->type)))
- return;
-
if (raw_res_spin_lock_irqsave(&l->lock, flags)) {
WRITE_ONCE(node->pending_free, 1);
return;
@@ -542,7 +539,7 @@ static void bpf_common_lru_push_free(struct bpf_lru *lru,
return;
}
- if (unlikely(node->type != BPF_LRU_LOCAL_LIST_T_PENDING)) {
+ if (unlikely(READ_ONCE(node->type) != BPF_LRU_LOCAL_LIST_T_PENDING)) {
raw_res_spin_unlock_irqrestore(&loc_l->lock,
flags);
goto check_lru_list;
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 82c5988417a0..f58dce888ff4 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -116,8 +116,11 @@ void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
}
#endif
+BTF_ID_LIST_SINGLE(bpf_lsm_inode_init_security_btf_id, func,
+ bpf_lsm_inode_init_security)
+
int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
- const struct bpf_prog *prog)
+ struct bpf_prog *prog)
{
u32 btf_id = prog->aux->attach_btf_id;
const char *func_name = prog->aux->attach_func_name;
@@ -140,6 +143,23 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
return -EINVAL;
}
+ if (btf_id == bpf_lsm_inode_init_security_btf_id[0]) {
+ /*
+ * inode, dir, qstr, xattrs, xattr_count
+ *
+ * The trusted pointer this hands the program is only as
+ * trustworthy as the context it is loaded from, which
+ * lsm_verifier_ops keeps read-only.
+ */
+ static const struct bpf_ctx_arg_aux xattr_count_arg_info = {
+ .offset = 4 * sizeof(u64),
+ .reg_type = PTR_TO_MEM | MEM_RDONLY | PTR_TRUSTED,
+ .mem_size = sizeof(int),
+ };
+
+ return bpf_prog_ctx_arg_info_init(prog, &xattr_count_arg_info, 1);
+ }
+
return 0;
}
@@ -414,8 +434,8 @@ const struct bpf_prog_ops lsm_prog_ops = {
};
const struct bpf_verifier_ops lsm_verifier_ops = {
- .get_func_proto = bpf_lsm_func_proto,
- .is_valid_access = btf_ctx_access,
+ .get_func_proto = bpf_lsm_func_proto,
+ .is_valid_access = bpf_tracing_btf_ctx_access,
};
/* hooks return 0 or 1 */
diff --git a/kernel/bpf/bpf_lsm_proto.c b/kernel/bpf/bpf_lsm_proto.c
index 44a54fd8045e..b81179ae2e08 100644
--- a/kernel/bpf/bpf_lsm_proto.c
+++ b/kernel/bpf/bpf_lsm_proto.c
@@ -4,6 +4,7 @@
*/
#include <linux/fs.h>
+#include <linux/xattr.h>
#include <linux/bpf_lsm.h>
/*
@@ -12,8 +13,22 @@
* explicitly enforces that BPF LSM programs check for NULL before attempting to
* dereference it.
*/
-int bpf_lsm_mmap_file(struct file *file__nullable, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
+noinline int bpf_lsm_mmap_file(struct file *file__nullable, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
{
return 0;
}
+
+/*
+ * Strong definition of the inode_init_security() BPF LSM hook. Both the
+ * qstr and the xattr array are NULL for some callers, so the __nullable
+ * suffix marks it as PTR_MAYBE_NULL. BPF LSM programs have to check before
+ * dereferencing them or handing them to bpf_inode_init_xattr().
+ */
+noinline int bpf_lsm_inode_init_security(struct inode *inode, struct inode *dir,
+ const struct qstr *qstr__nullable,
+ struct xattr *xattrs__nullable,
+ int *xattr_count)
+{
+ return -EOPNOTSUPP;
+}
diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
index d7c3030bc63b..1178acd72296 100644
--- a/kernel/bpf/bpf_struct_ops.c
+++ b/kernel/bpf/bpf_struct_ops.c
@@ -13,6 +13,7 @@
#include <linux/btf_ids.h>
#include <linux/rcupdate_wait.h>
#include <linux/poll.h>
+#include <linux/bpf-cgroup.h>
struct bpf_struct_ops_value {
struct bpf_struct_ops_common_value common;
@@ -57,7 +58,7 @@ struct bpf_struct_ops_map {
struct bpf_struct_ops_link {
struct bpf_link link;
- struct bpf_map __rcu *map;
+ struct bpf_map *map;
wait_queue_head_t wait_hup;
};
@@ -1024,9 +1025,18 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)
bpf_map_area_free(st_map);
}
+static void bpf_struct_ops_map_free_pre_rcu(struct bpf_map *map)
+{
+ struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
+
+ bpf_struct_ops_map_del_ksyms(st_map);
+}
+
static void bpf_struct_ops_map_free(struct bpf_map *map)
{
struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
+ struct bpf_struct_ops *st_ops = st_map->st_ops_desc->st_ops;
+ bool tasks_rcu = st_ops->free_after_tasks_rcu_gp;
/* st_ops->owner was acquired during map_alloc to implicitly holds
* the btf's refcnt. The acquire was only done when btf_is_module()
@@ -1037,24 +1047,8 @@ static void bpf_struct_ops_map_free(struct bpf_map *map)
bpf_struct_ops_map_dissoc_progs(st_map);
- bpf_struct_ops_map_del_ksyms(st_map);
-
- /* The struct_ops's function may switch to another struct_ops.
- *
- * For example, bpf_tcp_cc_x->init() may switch to
- * another tcp_cc_y by calling
- * setsockopt(TCP_CONGESTION, "tcp_cc_y").
- * During the switch, bpf_struct_ops_put(tcp_cc_x) is called
- * and its refcount may reach 0 which then free its
- * trampoline image while tcp_cc_x is still running.
- *
- * A vanilla rcu gp is to wait for all bpf-tcp-cc prog
- * to finish. bpf-tcp-cc prog is non sleepable.
- * A rcu_tasks gp is to wait for the last few insn
- * in the tramopline image to finish before releasing
- * the trampoline image.
- */
- synchronize_rcu_mult(call_rcu, call_rcu_tasks);
+ if (tasks_rcu && IS_ENABLED(CONFIG_TASKS_RCU))
+ synchronize_rcu_tasks();
__bpf_struct_ops_map_free(map);
}
@@ -1123,6 +1117,11 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
goto errout;
}
+ if (st_ops_desc->st_ops->cgroup_atype && !(attr->map_flags & BPF_F_LINK)) {
+ ret = -EOPNOTSUPP;
+ goto errout;
+ }
+
vt = st_ops_desc->value_type;
if (attr->value_size != vt->size) {
ret = -EINVAL;
@@ -1163,6 +1162,8 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
mutex_init(&st_map->lock);
bpf_map_init_from_attr(map, attr);
+ map->free_after_mult_rcu_gp = st_ops_desc->st_ops->free_after_mult_rcu_gp;
+ map->free_after_rcu_gp = true;
return map;
@@ -1195,6 +1196,7 @@ const struct bpf_map_ops bpf_struct_ops_map_ops = {
.map_alloc_check = bpf_struct_ops_map_alloc_check,
.map_alloc = bpf_struct_ops_map_alloc,
.map_free = bpf_struct_ops_map_free,
+ .map_free_pre_rcu = bpf_struct_ops_map_free_pre_rcu,
.map_get_next_key = bpf_struct_ops_map_get_next_key,
.map_lookup_elem = bpf_struct_ops_map_lookup_elem,
.map_delete_elem = bpf_struct_ops_map_delete_elem,
@@ -1281,7 +1283,31 @@ int bpf_struct_ops_for_each_prog(const void *kdata,
}
EXPORT_SYMBOL_GPL(bpf_struct_ops_for_each_prog);
-static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)
+void *bpf_struct_ops_map_kdata(struct bpf_map *map)
+{
+ struct bpf_struct_ops_map *st_map;
+
+ st_map = container_of(map, struct bpf_struct_ops_map, map);
+ return st_map->kvalue.data;
+}
+
+int bpf_struct_ops_map_cgroup_atype(struct bpf_map *map)
+{
+ struct bpf_struct_ops_map *st_map;
+
+ st_map = container_of(map, struct bpf_struct_ops_map, map);
+ return st_map->st_ops_desc->st_ops->cgroup_atype;
+}
+
+void *bpf_struct_ops_map_cfi_stubs(struct bpf_map *map)
+{
+ struct bpf_struct_ops_map *st_map;
+
+ st_map = container_of(map, struct bpf_struct_ops_map, map);
+ return st_map->st_ops_desc->st_ops->cfi_stubs;
+}
+
+bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)
{
struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
@@ -1297,8 +1323,7 @@ static void bpf_struct_ops_map_link_dealloc(struct bpf_link *link)
struct bpf_struct_ops_map *st_map;
st_link = container_of(link, struct bpf_struct_ops_link, link);
- st_map = (struct bpf_struct_ops_map *)
- rcu_dereference_protected(st_link->map, true);
+ st_map = (struct bpf_struct_ops_map *)st_link->map;
if (st_map) {
st_map->st_ops_desc->st_ops->unreg(&st_map->kvalue.data, link);
bpf_map_put(&st_map->map);
@@ -1313,11 +1338,11 @@ static void bpf_struct_ops_map_link_show_fdinfo(const struct bpf_link *link,
struct bpf_map *map;
st_link = container_of(link, struct bpf_struct_ops_link, link);
- rcu_read_lock();
- map = rcu_dereference(st_link->map);
+ mutex_lock(&update_mutex);
+ map = st_link->map;
if (map)
seq_printf(seq, "map_id:\t%d\n", map->id);
- rcu_read_unlock();
+ mutex_unlock(&update_mutex);
}
static int bpf_struct_ops_map_link_fill_link_info(const struct bpf_link *link,
@@ -1327,11 +1352,31 @@ static int bpf_struct_ops_map_link_fill_link_info(const struct bpf_link *link,
struct bpf_map *map;
st_link = container_of(link, struct bpf_struct_ops_link, link);
- rcu_read_lock();
- map = rcu_dereference(st_link->map);
+ mutex_lock(&update_mutex);
+ map = st_link->map;
if (map)
info->struct_ops.map_id = map->id;
- rcu_read_unlock();
+ mutex_unlock(&update_mutex);
+ return 0;
+}
+
+int bpf_struct_ops_link_update_check(struct bpf_map *new_map,
+ struct bpf_map *old_map,
+ struct bpf_map *expected_old_map)
+{
+ struct bpf_struct_ops_map *st_map, *old_st_map;
+
+ if (!old_map)
+ return -ENOLINK;
+ if (expected_old_map && old_map != expected_old_map)
+ return -EPERM;
+
+ st_map = container_of(new_map, struct bpf_struct_ops_map, map);
+ old_st_map = container_of(old_map, struct bpf_struct_ops_map, map);
+ /* The new and old struct_ops must be the same type. */
+ if (st_map->st_ops_desc != old_st_map->st_ops_desc)
+ return -EINVAL;
+
return 0;
}
@@ -1353,30 +1398,19 @@ static int bpf_struct_ops_map_link_update(struct bpf_link *link, struct bpf_map
return -EOPNOTSUPP;
mutex_lock(&update_mutex);
-
- old_map = rcu_dereference_protected(st_link->map, lockdep_is_held(&update_mutex));
- if (!old_map) {
- err = -ENOLINK;
- goto err_out;
- }
- if (expected_old_map && old_map != expected_old_map) {
- err = -EPERM;
+ old_map = st_link->map;
+ err = bpf_struct_ops_link_update_check(new_map, old_map, expected_old_map);
+ if (err)
goto err_out;
- }
old_st_map = container_of(old_map, struct bpf_struct_ops_map, map);
- /* The new and old struct_ops must be the same type. */
- if (st_map->st_ops_desc != old_st_map->st_ops_desc) {
- err = -EINVAL;
- goto err_out;
- }
err = st_map->st_ops_desc->st_ops->update(st_map->kvalue.data, old_st_map->kvalue.data, link);
if (err)
goto err_out;
bpf_map_inc(new_map);
- rcu_assign_pointer(st_link->map, new_map);
+ WRITE_ONCE(st_link->map, new_map);
bpf_map_put(old_map);
err_out:
@@ -1393,7 +1427,7 @@ static int bpf_struct_ops_map_link_detach(struct bpf_link *link)
mutex_lock(&update_mutex);
- map = rcu_dereference_protected(st_link->map, lockdep_is_held(&update_mutex));
+ map = st_link->map;
if (!map) {
mutex_unlock(&update_mutex);
return 0;
@@ -1402,7 +1436,7 @@ static int bpf_struct_ops_map_link_detach(struct bpf_link *link)
st_map->st_ops_desc->st_ops->unreg(&st_map->kvalue.data, link);
- RCU_INIT_POINTER(st_link->map, NULL);
+ WRITE_ONCE(st_link->map, NULL);
/* Pair with bpf_map_get() in bpf_struct_ops_link_create() or
* bpf_map_inc() in bpf_struct_ops_map_link_update().
*/
@@ -1422,7 +1456,7 @@ static __poll_t bpf_struct_ops_map_link_poll(struct file *file,
poll_wait(file, &st_link->wait_hup, pts);
- return rcu_access_pointer(st_link->map) ? 0 : EPOLLHUP;
+ return READ_ONCE(st_link->map) ? 0 : EPOLLHUP;
}
static const struct bpf_link_ops bpf_struct_ops_map_lops = {
@@ -1440,6 +1474,7 @@ int bpf_struct_ops_link_create(union bpf_attr *attr)
struct bpf_link_primer link_primer;
struct bpf_struct_ops_map *st_map;
struct bpf_map *map;
+ int cgroup_atype;
int err;
map = bpf_map_get(attr->link_create.map_fd);
@@ -1453,6 +1488,19 @@ int bpf_struct_ops_link_create(union bpf_attr *attr)
goto err_out;
}
+ cgroup_atype = st_map->st_ops_desc->st_ops->cgroup_atype;
+ if (cgroup_atype) {
+ err = cgroup_bpf_struct_ops_attach(map, attr);
+ bpf_map_put(map);
+ return err;
+ }
+
+ if (memchr_inv(&attr->link_create.cgroup, 0, sizeof(attr->link_create.cgroup)) ||
+ attr->link_create.target_fd) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
link = kzalloc_obj(*link, GFP_USER);
if (!link) {
err = -ENOMEM;
@@ -1478,7 +1526,7 @@ int bpf_struct_ops_link_create(union bpf_attr *attr)
link = NULL;
goto err_out;
}
- RCU_INIT_POINTER(link->map, map);
+ link->map = map;
mutex_unlock(&update_mutex);
return bpf_link_settle(&link_primer);
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index d870bc5e50bc..538129ed2418 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -20,15 +20,17 @@
#include <linux/btf.h>
#include <linux/btf_ids.h>
#include <linux/bpf.h>
+#include <linux/bpf-cgroup.h>
#include <linux/bpf_lsm.h>
#include <linux/skmsg.h>
#include <linux/perf_event.h>
#include <linux/bsearch.h>
-#include <linux/kobject.h>
#include <linux/string.h>
-#include <linux/sysfs.h>
#include <linux/overflow.h>
#include <linux/bitops.h>
+#include <linux/unaligned.h>
+#include <linux/vmalloc.h>
+#include <crypto/sha2.h>
#include <net/netfilter/nf_bpf_link.h>
@@ -345,6 +347,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
const char *btf_type_str(const struct btf_type *t)
@@ -517,11 +522,27 @@ static bool btf_type_is_decl_tag(const struct btf_type *t)
return BTF_INFO_KIND(t->info) == BTF_KIND_DECL_TAG;
}
+static bool btf_type_is_loc_param(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PARAM;
+}
+
+static bool btf_type_is_loc_proto(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PROTO;
+}
+
+static bool btf_type_is_locsec(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOCSEC;
+}
+
static bool btf_type_nosize(const struct btf_type *t)
{
return btf_type_is_void(t) || btf_type_is_fwd(t) ||
btf_type_is_func(t) || btf_type_is_func_proto(t) ||
- btf_type_is_decl_tag(t);
+ btf_type_is_decl_tag(t) || btf_type_is_loc_param(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t);
}
static bool btf_type_nosize_or_null(const struct btf_type *t)
@@ -768,7 +789,9 @@ static bool btf_type_is_resolve_source_only(const struct btf_type *t)
{
return btf_type_is_var(t) ||
btf_type_is_decl_tag(t) ||
- btf_type_is_datasec(t);
+ btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) ||
+ btf_type_is_locsec(t);
}
/* What types need to be resolved?
@@ -796,7 +819,9 @@ static bool btf_type_needs_resolve(const struct btf_type *t)
btf_type_is_var(t) ||
btf_type_is_func(t) ||
btf_type_is_decl_tag(t) ||
- btf_type_is_datasec(t);
+ btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) ||
+ btf_type_is_locsec(t);
}
/* t->size can be used */
@@ -877,16 +902,6 @@ static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
return offset < btf->hdr.str_len;
}
-static bool __btf_name_char_ok(char c, bool first)
-{
- if ((first ? !isalpha(c) :
- !isalnum(c)) &&
- c != '_' &&
- c != '.')
- return false;
- return true;
-}
-
const char *btf_str_by_offset(const struct btf *btf, u32 offset)
{
while (offset < btf->start_str_off)
@@ -899,20 +914,21 @@ const char *btf_str_by_offset(const struct btf *btf, u32 offset)
return NULL;
}
+/* Names in BTF of Rust are not C identifiers. Allow any printable character */
static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
{
/* offset must be valid */
const char *src = btf_str_by_offset(btf, offset);
const char *src_limit;
- if (!__btf_name_char_ok(*src, true))
+ if (!isprint(*src))
return false;
/* set a limit on identifier length */
src_limit = src + KSYM_NAME_LEN;
src++;
while (*src && src < src_limit) {
- if (!__btf_name_char_ok(*src, false))
+ if (!isprint(*src))
return false;
src++;
}
@@ -1065,6 +1081,8 @@ static const struct btf_type *btf_type_skip_qualifiers(const struct btf *btf,
}
#define BTF_SHOW_MAX_ITER 10
+/* nested structs and arrays shown; __btf_show_indent() has as many columns */
+#define BTF_SHOW_MAX_DEPTH 32
#define BTF_KIND_BIT(kind) (1ULL << kind)
@@ -1458,8 +1476,18 @@ static void *btf_show_start_aggr_type(struct btf_show *show,
const struct btf_type *t,
u32 type_id, void *data)
{
- void *safe_data = btf_show_start_type(show, t, type_id, data);
+ void *safe_data;
+ /*
+ * Structs and arrays are shown by recursion. BTF loading bounds the
+ * depth of one resolve walk, not how deep resolved types may nest.
+ */
+ if (show->state.depth >= BTF_SHOW_MAX_DEPTH) {
+ show->state.status = -E2BIG;
+ return NULL;
+ }
+
+ safe_data = btf_show_start_type(show, t, type_id, data);
if (!safe_data)
return safe_data;
@@ -1920,7 +1948,7 @@ void btf_set_base_btf(struct btf *btf, const struct btf *base_btf)
{
btf->base_btf = (struct btf *)base_btf;
btf->start_id = btf_nr_types(base_btf);
- btf->start_str_off = base_btf->hdr.str_len;
+ btf->start_str_off = base_btf->start_str_off + base_btf->hdr.str_len;
}
static int env_resolve_init(struct btf_verifier_env *env)
@@ -2064,7 +2092,8 @@ static const struct resolve_vertex *env_stack_peak(struct btf_verifier_env *env)
* *elem_id: id of u32
* *total_nelems: (x * y). Hence, individual elem size is
* (*type_size / *total_nelems)
- * *type_id: id of type if it's changed within the function, 0 if not
+ * *type_id: ID of the returned type if leading modifiers were stripped;
+ * otherwise left unchanged.
*
* type: is not an array (e.g. const struct X)
* return type: type "struct X"
@@ -2072,7 +2101,8 @@ static const struct resolve_vertex *env_stack_peak(struct btf_verifier_env *env)
* *elem_type: same as return type ("struct X")
* *elem_id: 0
* *total_nelems: 1
- * *type_id: id of type if it's changed within the function, 0 if not
+ * *type_id: ID of the returned type if leading modifiers were stripped;
+ * otherwise left unchanged.
*/
static const struct btf_type *
__btf_resolve_size(const struct btf *btf, const struct btf_type *type,
@@ -2105,7 +2135,8 @@ __btf_resolve_size(const struct btf *btf, const struct btf_type *type,
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_TYPE_TAG:
- id = type->type;
+ if (!array_type)
+ id = type->type;
type = btf_type_by_id(btf, type->type);
break;
@@ -3538,6 +3569,28 @@ static int btf_type_tag_walk(const struct btf *btf,
return 0;
}
+/* Whether the pointee of pointer type @t carries the type tag @name */
+static bool btf_ptr_has_type_tag(const struct btf *btf, const struct btf_type *t, const char *name)
+{
+ if (!btf_type_is_ptr(t))
+ return false;
+
+ for (t = btf_type_by_id(btf, t->type); btf_type_is_modifier(t);
+ t = btf_type_by_id(btf, t->type)) {
+ if (!btf_type_is_type_tag(t) || btf_type_kflag(t))
+ continue;
+ if (!strcmp(__btf_name_by_offset(btf, t->name_off), name))
+ return true;
+ }
+
+ return false;
+}
+
+bool btf_type_is_arena_ptr(const struct btf *btf, const struct btf_type *t)
+{
+ return btf_ptr_has_type_tag(btf, t, "arena");
+}
+
static int btf_find_kptr(const struct btf *btf, const struct btf_type *t,
u32 off, int sz, struct btf_field_info *info, u32 field_mask)
{
@@ -3679,6 +3732,7 @@ static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_
{ BPF_TIMER, "bpf_timer", true },
{ BPF_WORKQUEUE, "bpf_wq", true },
{ BPF_TASK_WORK, "bpf_task_work", true },
+ { BPF_RCU_HEAD, "bpf_rcu_head", true },
{ BPF_LIST_HEAD, "bpf_list_head", false },
{ BPF_LIST_NODE, "bpf_list_node", false },
{ BPF_RB_ROOT, "bpf_rb_root", false },
@@ -3864,6 +3918,7 @@ static int btf_find_field_one(const struct btf *btf,
case BPF_RB_NODE:
case BPF_REFCOUNT:
case BPF_TASK_WORK:
+ case BPF_RCU_HEAD:
ret = btf_find_struct(btf, var_type, off, sz, field_type,
info_cnt ? &info[0] : &tmp);
if (ret < 0)
@@ -4159,6 +4214,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
rec->wq_off = -EINVAL;
rec->refcount_off = -EINVAL;
rec->task_work_off = -EINVAL;
+ rec->rcu_head_off = -EINVAL;
for (i = 0; i < cnt; i++) {
field_type_size = btf_field_type_size(info_arr[i].type);
if (info_arr[i].off + field_type_size > value_size) {
@@ -4202,6 +4258,10 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
WARN_ON_ONCE(rec->task_work_off >= 0);
rec->task_work_off = rec->fields[i].offset;
break;
+ case BPF_RCU_HEAD:
+ WARN_ON_ONCE(rec->rcu_head_off >= 0);
+ rec->rcu_head_off = rec->fields[i].offset;
+ break;
case BPF_REFCOUNT:
WARN_ON_ONCE(rec->refcount_off >= 0);
/* Cache offset for faster lookup at runtime */
@@ -4747,6 +4807,336 @@ static const struct btf_kind_operations enum64_ops = {
.show = btf_enum64_show,
};
+static s32 btf_loc_param_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 size, meta_needed, vlen = btf_vlen(t);
+
+ meta_needed = sizeof(*p) + sizeof(__u32) * vlen;
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+ size = t->size;
+ if (!size || size > 16) {
+ btf_verifier_log_type(env, t, "Unexpected size");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+ /* All LOC_PARAMs have vlen of at least 1 */
+ if (vlen < 1)
+ goto invalid_vlen;
+
+ switch (p->flags) {
+ case BTF_LOC_PARAM_CONST:
+ case BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_SIGNED:
+ case BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_ADDR:
+ case BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_SIGNED:
+ if (vlen < 1 || vlen > 2)
+ goto invalid_vlen;
+ if (!size || size > sizeof(__u64))
+ goto invalid_vlen;
+ /* Ensure size/vlen are consistent */
+ if (vlen != (size > sizeof(__u32) ? 2 : 1))
+ goto invalid_vlen;
+ break;
+ case BTF_LOC_PARAM_REG:
+ /* Reg or reg/reg pair */
+ if (vlen > 2)
+ goto invalid_vlen;
+ break;
+ case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF:
+ /* A reg deref without offset can only have vlen 1 */
+ if (vlen != 1)
+ goto invalid_vlen;
+ break;
+ case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET:
+ case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET | BTF_LOC_PARAM_SIGNED:
+ case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET:
+ case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET |
+ BTF_LOC_PARAM_SIGNED:
+ if (vlen < 2 || vlen > 3)
+ goto invalid_vlen;
+ break;
+ default:
+ btf_verifier_log_type(env, t, "Invalid flags");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+
+invalid_vlen:
+ btf_verifier_log_type(env, t, "Invalid vlen");
+ return -EINVAL;
+}
+
+static void btf_loc_param_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 i, vlen = btf_vlen(t);
+
+ btf_verifier_log(env, "size=%u vlen=%u flags=0x%x", t->size, vlen, p->flags);
+ for (i = 0; i < vlen; i++)
+ btf_verifier_log(env, ", %u", p->values[i]);
+}
+
+static const struct btf_kind_operations loc_param_ops = {
+ .check_meta = btf_loc_param_check_meta,
+ .resolve = btf_df_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_param_log,
+ .show = btf_df_show,
+};
+
+static s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 meta_needed;
+
+ meta_needed = sizeof(__u32) * btf_type_vlen(t);
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+}
+
+static void btf_loc_proto_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+
+ btf_verifier_log(env, "vlen=%u", nr_params);
+ for (i = 0; i < nr_params; i++, params++)
+ btf_verifier_log(env, ", %u", *params);
+}
+
+static int btf_loc_proto_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+ struct btf *btf = env->btf;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid loc_proto type");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_params; i++) {
+ const struct btf_type *param_type;
+ u32 param_type_id = params[i];
+
+ if (!param_type_id)
+ continue;
+
+ param_type = btf_type_by_id(btf, param_type_id);
+ if (!param_type || !btf_type_is_loc_param(param_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_param#%u", i + 1);
+ return -EINVAL;
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations loc_proto_ops = {
+ .check_meta = btf_loc_proto_check_meta,
+ .resolve = btf_loc_proto_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_proto_log,
+ .show = btf_df_show,
+};
+
+__printf(4, 5)
+static void btf_verifier_log_loc(struct btf_verifier_env *env,
+ const struct btf_type *locsec_type,
+ const struct btf_loc *loc,
+ const char *fmt, ...)
+{
+ struct bpf_verifier_log *log = &env->log;
+ va_list args;
+
+ if (!bpf_verifier_log_needed(log))
+ return;
+ if (log->level == BPF_LOG_KERNEL && !fmt)
+ return;
+ if (env->phase != CHECK_META)
+ btf_verifier_log_type(env, locsec_type, NULL);
+
+ __btf_verifier_log(log, "\t func=%u loc_proto=%u offset=%u",
+ loc->func, loc->loc_proto, loc->offset);
+ if (fmt && *fmt) {
+ __btf_verifier_log(log, " ");
+ va_start(args, fmt);
+ bpf_verifier_vlog(log, fmt, args);
+ va_end(args);
+ }
+
+ __btf_verifier_log(log, "\n");
+}
+
+static s32 btf_locsec_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 i, meta_needed, vlen = btf_type_vlen(t);
+ const struct btf_loc *loc;
+
+ meta_needed = sizeof(struct btf_loc) * vlen;
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ if (!t->name_off ||
+ !btf_name_valid_section(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ for_each_loc(i, t, loc) {
+ /* A loc func, loc proto cannot be in type void */
+ if (!loc->func || !BTF_TYPE_ID_VALID(loc->func)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid func");
+ return -EINVAL;
+ }
+ if (!loc->loc_proto || !BTF_TYPE_ID_VALID(loc->loc_proto)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid loc_proto");
+ return -EINVAL;
+ }
+ btf_verifier_log_loc(env, t, loc, NULL);
+ }
+
+ return meta_needed;
+}
+
+static void btf_locsec_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ btf_verifier_log(env, "vlen=%u", btf_type_vlen(t));
+}
+
+static int btf_locsec_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const struct btf_loc *loc;
+ struct btf *btf = env->btf;
+ u32 i;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid locsec type");
+ return -EINVAL;
+ }
+
+ env->resolve_mode = RESOLVE_TBD;
+ for (i = v->next_member, loc = btf_type_loc_secinfo(t) + i;
+ i < btf_type_vlen(t); i++, loc++) {
+ const struct btf_type *func_type, *func_proto_type;
+ const struct btf_type *loc_proto_type;
+ u32 func_type_id = loc->func;
+ u32 loc_proto_type_id = loc->loc_proto;
+ u32 proto_vlen;
+
+ func_type = btf_type_by_id(btf, func_type_id);
+ if (!func_type || !btf_type_is_func(func_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid func#%u", i + 1);
+ return -EINVAL;
+ }
+ func_proto_type = btf_type_by_id(btf, func_type->type);
+ if (!func_proto_type || !btf_type_is_func_proto(func_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid func#%u", i + 1);
+ return -EINVAL;
+ }
+ proto_vlen = btf_vlen(func_proto_type);
+
+ if (!env_type_is_resolved(env, func_type_id)) {
+ env_stack_set_next_member(env, i);
+ return env_stack_push(env, func_type, func_type_id);
+ }
+
+ loc_proto_type = btf_type_by_id(btf, loc_proto_type_id);
+ if (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_proto#%u", i + 1);
+ return -EINVAL;
+ }
+ if (proto_vlen != btf_vlen(loc_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Mismatched vlen for loc_proto#%u", i + 1);
+ return -EINVAL;
+ }
+
+ if (!env_type_is_resolved(env, loc_proto_type_id)) {
+ env_stack_set_next_member(env, i + 1);
+ return env_stack_push(env, loc_proto_type,
+ loc_proto_type_id);
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations locsec_ops = {
+ .check_meta = btf_locsec_check_meta,
+ .resolve = btf_locsec_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_locsec_log,
+ .show = btf_df_show,
+};
+
static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
const struct btf_type *t,
u32 meta_left)
@@ -5029,7 +5419,7 @@ static int btf_datasec_resolve(struct btf_verifier_env *env,
env->resolve_mode = RESOLVE_TBD;
for_each_vsi_from(i, v->next_member, v->t, vsi) {
- u32 var_type_id = vsi->type, type_id, type_size = 0;
+ u32 var_type_id = vsi->type, type_id;
const struct btf_type *var_type = btf_type_by_id(env->btf,
var_type_id);
if (!var_type || !btf_type_is_var(var_type)) {
@@ -5044,16 +5434,16 @@ static int btf_datasec_resolve(struct btf_verifier_env *env,
return env_stack_push(env, var_type, var_type_id);
}
+ /*
+ * The variable can be smaller than its type. It's a piece of
+ * a variable that the compiler split then, with the type of
+ * the whole variable.
+ */
type_id = var_type->type;
- if (!btf_type_id_size(btf, &type_id, &type_size)) {
+ if (!btf_type_id_size(btf, &type_id, NULL)) {
btf_verifier_log_vsi(env, v->t, vsi, "Invalid type");
return -EINVAL;
}
-
- if (vsi->size < type_size) {
- btf_verifier_log_vsi(env, v->t, vsi, "Invalid size");
- return -EINVAL;
- }
}
env_stack_pop_resolved(env, 0, 0);
@@ -5066,6 +5456,16 @@ static void btf_datasec_log(struct btf_verifier_env *env,
btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
}
+/* A piece of a variable is smaller than its type, which is the one of the whole variable */
+static bool btf_var_is_piece(const struct btf *btf, const struct btf_type *var,
+ const struct btf_var_secinfo *vsi)
+{
+ u32 size;
+
+ return IS_ERR(btf_resolve_size(btf, btf_type_by_id(btf, var->type), &size)) ||
+ vsi->size < size;
+}
+
static void btf_datasec_show(const struct btf *btf,
const struct btf_type *t, u32 type_id,
void *data, u8 bits_offset,
@@ -5073,6 +5473,7 @@ static void btf_datasec_show(const struct btf *btf,
{
const struct btf_var_secinfo *vsi;
const struct btf_type *var;
+ bool comma = false;
u32 i;
if (!btf_show_start_type(show, t, type_id, data))
@@ -5082,8 +5483,12 @@ static void btf_datasec_show(const struct btf *btf,
__btf_name_by_offset(btf, t->name_off));
for_each_vsi(i, t, vsi) {
var = btf_type_by_id(btf, vsi->type);
- if (i)
+ /* there is less data than the type takes */
+ if (btf_var_is_piece(btf, var, vsi))
+ continue;
+ if (comma)
btf_show(show, ",");
+ comma = true;
btf_type_ops(var)->show(btf, var, vsi->type,
data + vsi->offset, bits_offset, show);
}
@@ -5384,6 +5789,10 @@ static int btf_func_check(struct btf_verifier_env *env,
return -EINVAL;
}
+ /* Rust leaves out names of some arguments of static functions */
+ if (btf_func_linkage(t) == BTF_FUNC_STATIC)
+ return 0;
+
args = (const struct btf_param *)(proto_type + 1);
nr_args = btf_type_vlen(proto_type);
for (i = 0; i < nr_args; i++) {
@@ -5416,6 +5825,9 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = &decl_tag_ops,
[BTF_KIND_TYPE_TAG] = &modifier_ops,
[BTF_KIND_ENUM64] = &enum64_ops,
+ [BTF_KIND_LOC_PARAM] = &loc_param_ops,
+ [BTF_KIND_LOC_PROTO] = &loc_proto_ops,
+ [BTF_KIND_LOCSEC] = &locsec_ops,
};
static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -5490,7 +5902,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
if (!env_type_is_resolved(env, type_id))
return false;
- if (btf_type_is_struct(t) || btf_type_is_datasec(t))
+ if (btf_type_is_struct(t) || btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t))
return !btf_resolved_type_id(btf, type_id) &&
!btf_resolved_type_size(btf, type_id);
@@ -5815,7 +6228,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env)
return btf_check_sec_info(env, btf_data_size);
}
-static const char *alloc_obj_fields[] = {
+static const char * const alloc_obj_fields[] = {
"bpf_spin_lock",
"bpf_list_head",
"bpf_list_node",
@@ -6530,15 +6943,137 @@ __u32 btf_relocate_id(const struct btf *btf, __u32 id)
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+static struct btf *btf_parse_split(struct btf_verifier_env *env,
+ const char *name, const void *data,
+ unsigned int data_size, struct btf *base_btf)
+{
+ struct btf *btf;
+ int err;
+
+ btf = kzalloc_obj(*btf, GFP_KERNEL | __GFP_NOWARN);
+ if (!btf)
+ return ERR_PTR(-ENOMEM);
+ env->btf = btf;
+
+ btf_set_base_btf(btf, base_btf);
+ btf->kernel_btf = true;
+ btf->named_start_id = 0;
+ strscpy(btf->name, name);
+
+ btf->data = kvmemdup(data, data_size, GFP_KERNEL | __GFP_NOWARN);
+ if (!btf->data) {
+ err = -ENOMEM;
+ goto errout;
+ }
+ btf->data_size = data_size;
+
+ err = btf_parse_hdr(env);
+ if (err)
+ goto errout;
+
+ btf->nohdr_data = btf->data + btf->hdr.hdr_len;
+
+ err = btf_parse_str_sec(env);
+ if (err)
+ goto errout;
+
+ err = btf_check_all_metas(env);
+ if (err)
+ goto errout;
+
+ err = btf_check_modifier_chain_length(env, btf, btf_nr_types(base_btf));
+ if (err)
+ goto errout;
+
+ return btf;
+
+errout:
+ btf_free(btf);
+ return ERR_PTR(err);
+}
+
+static int btf_rebase_inline(struct btf *inline_btf,
+ const struct btf *module_btf,
+ const u32 *module_id_map,
+ const u32 *module_str_map,
+ u32 old_module_type_cnt)
+{
+ u32 old_start_id = inline_btf->start_id;
+ u32 old_start_str_off = inline_btf->start_str_off;
+ u32 old_module_start_str_off = old_start_str_off - module_btf->hdr.str_len;
+ u32 new_start_id = btf_nr_types(module_btf);
+ u32 new_start_str_off = module_btf->start_str_off + module_btf->hdr.str_len;
+ s64 id_delta = (s64)new_start_id - old_start_id;
+ s64 str_delta = (s64)new_start_str_off - old_start_str_off;
+ u32 i;
+
+ /*
+ * The inline BTF was parsed relative to the original module BTF. Its
+ * base IDs must therefore use the map generated when that BTF was
+ * relocated, while IDs for inline-local types only move by the change
+ * in the module BTF's starting ID.
+ */
+ for (i = 0; i < inline_btf->nr_types; i++) {
+ struct btf_field_iter it;
+ struct btf_type *t = inline_btf->types[i];
+ u32 *id, *str_off;
+ int err;
+
+ err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_IDS);
+ if (err)
+ return err;
+ while ((id = btf_field_iter_next(&it))) {
+ if (!*id)
+ continue;
+ if (*id < old_module_type_cnt) {
+ if (module_id_map)
+ *id = module_id_map[*id];
+ } else if (*id >= old_start_id) {
+ *id += id_delta;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_STRS);
+ if (err)
+ return err;
+ while ((str_off = btf_field_iter_next(&it))) {
+ if (!*str_off)
+ continue;
+ if (*str_off < old_module_start_str_off) {
+ /* Vmlinux strings retain their offsets for in-tree modules. */
+ if (!module_id_map)
+ continue;
+ if (!module_str_map || !module_str_map[*str_off])
+ return -EINVAL;
+ *str_off = module_str_map[*str_off];
+ continue;
+ }
+ *str_off += str_delta;
+ }
+ }
+
+ btf_set_base_btf(inline_btf, module_btf);
+ btf_check_sorted(inline_btf);
+ return 0;
+}
+
static struct btf *btf_parse_module(const char *module_name, const void *data,
unsigned int data_size, void *base_data,
- unsigned int base_data_size)
+ unsigned int base_data_size,
+ const void *inline_data,
+ unsigned int inline_data_size,
+ void **relocated_inline_data)
{
- struct btf *btf = NULL, *vmlinux_btf, *base_btf = NULL;
+ struct btf *btf = NULL, *inline_btf = NULL, *vmlinux_btf, *base_btf = NULL;
struct btf_verifier_env *env = NULL;
struct bpf_verifier_log *log;
+ u32 *module_str_map = NULL;
+ u32 old_module_type_cnt;
int err = 0;
+ *relocated_inline_data = NULL;
vmlinux_btf = bpf_get_btf_vmlinux();
if (IS_ERR(vmlinux_btf))
return vmlinux_btf;
@@ -6562,67 +7097,68 @@ static struct btf *btf_parse_module(const char *module_name, const void *data,
base_btf = vmlinux_btf;
}
- btf = kzalloc_obj(*btf, GFP_KERNEL | __GFP_NOWARN);
- if (!btf) {
- err = -ENOMEM;
+ btf = btf_parse_split(env, module_name, data, data_size, base_btf);
+ if (IS_ERR(btf)) {
+ err = PTR_ERR(btf);
+ btf = NULL;
goto errout;
}
- env->btf = btf;
-
- btf->base_btf = base_btf;
- btf->start_id = base_btf->nr_types;
- btf->start_str_off = base_btf->hdr.str_len;
- btf->kernel_btf = true;
- btf->named_start_id = 0;
- strscpy(btf->name, module_name);
+ old_module_type_cnt = btf_nr_types(btf);
- btf->data = kvmemdup(data, data_size, GFP_KERNEL | __GFP_NOWARN);
- if (!btf->data) {
- err = -ENOMEM;
- goto errout;
+ if (inline_data_size) {
+ inline_btf = btf_parse_split(env, module_name, inline_data,
+ inline_data_size, btf);
+ if (IS_ERR(inline_btf)) {
+ pr_warn("failed to validate module [%s] inline BTF: %ld\n",
+ module_name, PTR_ERR(inline_btf));
+ inline_btf = NULL;
+ }
}
- btf->data_size = data_size;
-
- err = btf_parse_hdr(env);
- if (err)
- goto errout;
-
- btf->nohdr_data = btf->data + btf->hdr.hdr_len;
-
- err = btf_parse_str_sec(env);
- if (err)
- goto errout;
-
- err = btf_check_all_metas(env);
- if (err)
- goto errout;
-
- err = btf_check_modifier_chain_length(env, btf, btf_nr_types(base_btf));
- if (err)
- goto errout;
if (base_btf != vmlinux_btf) {
- err = btf_relocate(btf, vmlinux_btf, &btf->base_id_map);
+ err = btf_relocate(btf, vmlinux_btf, &btf->base_id_map,
+ &module_str_map);
if (err)
goto errout;
btf_free(base_btf);
base_btf = vmlinux_btf;
}
- btf_verifier_env_free(env);
+ if (inline_btf) {
+ err = btf_rebase_inline(inline_btf, btf, btf->base_id_map,
+ module_str_map, old_module_type_cnt);
+ if (err) {
+ pr_warn("failed to relocate module [%s] inline BTF: %d\n",
+ module_name, err);
+ btf_free(inline_btf);
+ } else {
+ *relocated_inline_data = inline_btf->data;
+ inline_btf->data = NULL;
+ btf_free(inline_btf);
+ }
+ }
+
+ /*
+ * With a distilled base, btf_relocate() replaces the base BTF and
+ * rewrites string offsets. Check ordering only after that final BTF
+ * view has been established, so named_start_id describes the BTF used
+ * by name lookups.
+ */
btf_check_sorted(btf);
+ btf_verifier_env_free(env);
+ kvfree(module_str_map);
refcount_set(&btf->refcnt, 1);
return btf;
errout:
+ kvfree(module_str_map);
btf_verifier_env_free(env);
+ if (inline_btf)
+ btf_free(inline_btf);
if (!IS_ERR(base_btf) && base_btf != vmlinux_btf)
btf_free(base_btf);
- if (btf) {
- kvfree(btf->data);
- kvfree(btf->types);
- kfree(btf);
- }
+ if (btf)
+ btf_free(btf);
return ERR_PTR(err);
}
@@ -7016,8 +7552,9 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
}
/*
- * Check for PTR_TO_RDONLY_BUF_OR_NULL, PTR_TO_RDWR_BUF_OR_NULL or
- * PTR_TO_ARENA (both nullable and non-nullable cases).
+ * Check for PTR_TO_RDONLY_BUF_OR_NULL, PTR_TO_RDWR_BUF_OR_NULL,
+ * PTR_TO_ARENA (both nullable and non-nullable cases) or fixed-size
+ * PTR_TO_MEM.
*/
for (i = 0; i < prog->aux->ctx_arg_info_size; i++) {
const struct bpf_ctx_arg_aux *ctx_arg_info = &prog->aux->ctx_arg_info[i];
@@ -7027,8 +7564,10 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
flag = type_flag(ctx_arg_info->reg_type);
if (ctx_arg_info->offset == off &&
(type == PTR_TO_ARENA ||
+ type == PTR_TO_MEM ||
(type == PTR_TO_BUF && (flag & PTR_MAYBE_NULL)))) {
info->reg_type = ctx_arg_info->reg_type;
+ info->mem_size = ctx_arg_info->mem_size;
return true;
}
}
@@ -7155,18 +7694,60 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
}
EXPORT_SYMBOL_GPL(btf_ctx_access);
+#define MAX_ARRAYS_WALK 16
+
+/* Description of an array crossed while walking a BTF access chain */
+struct array_access {
+ u32 id; /* BTF_KIND_ARRAY type id */
+ u32 off; /* offset of the access within the array */
+};
+
enum bpf_struct_walk_result {
/* < 0 error */
WALK_SCALAR = 0,
WALK_PTR,
WALK_PTR_UNTRUSTED,
+ WALK_PTR_ARENA,
WALK_STRUCT,
};
+static const struct btf_member *find_flex_member(const struct btf *btf, const struct btf_type *t)
+{
+ const struct btf_array *array;
+ const struct btf_type *mtype;
+ const struct btf_member *member;
+ u32 vlen = btf_type_vlen(t);
+
+ if (vlen == 0)
+ return NULL;
+
+ member = btf_type_member(t) + vlen - 1;
+ mtype = btf_type_skip_modifiers(btf, member->type, NULL);
+ if (!btf_type_is_array(mtype))
+ return NULL;
+
+ array = (const struct btf_array *)(mtype + 1);
+ if (array->nelems != 0)
+ return NULL;
+
+ return member;
+}
+
+static int push_array(struct array_access *arrays, u32 *arrays_cnt, struct array_access item)
+{
+ if (arrays == NULL)
+ return 0;
+ if (*arrays_cnt == MAX_ARRAYS_WALK)
+ return -1;
+ arrays[(*arrays_cnt)++] = item;
+ return 0;
+}
+
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, bool walk_flex_arrays)
+ const char **field_name, bool walk_flex_arrays,
+ struct array_access *arrays, u32 *arrays_cnt)
{
u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
const struct btf_type *mtype, *elem_type = NULL;
@@ -7201,22 +7782,19 @@ again:
/* If the last element is a variable size array, we may
* need to relax the rule.
*/
- if (vlen == 0)
+ member = find_flex_member(btf, t);
+ if (!member)
goto error;
- member = btf_type_member(t) + vlen - 1;
- mtype = btf_type_skip_modifiers(btf, member->type,
- NULL);
- if (!btf_type_is_array(mtype))
+ moff = __btf_member_bit_offset(t, member) / 8;
+ if (off < moff)
goto error;
+ mtype = btf_type_skip_modifiers(btf, member->type, &mid);
array_elem = (struct btf_array *)(mtype + 1);
- if (array_elem->nelems != 0)
- goto error;
- moff = __btf_member_bit_offset(t, member) / 8;
- if (off < moff)
- goto error;
+ if (push_array(arrays, arrays_cnt, (struct array_access){ mid, off - moff }))
+ return -E2BIG;
/* allow structure and integer */
t = btf_type_skip_modifiers(btf, array_elem->type,
@@ -7339,6 +7917,9 @@ error:
* the array's element as long as it is
* within the mtrue_end boundary.
*/
+ if (push_array(arrays, arrays_cnt,
+ (struct array_access){ mid, off - moff }))
+ return -E2BIG;
/* skip empty array */
if (moff == mtrue_end)
@@ -7399,6 +7980,38 @@ error:
id = ctx.id;
stype = ctx.t;
+ /*
+ * __nullable combines with any of the above: it only says
+ * that the pointer may be NULL. The caller decides whether
+ * that matters for the register type it derives.
+ */
+ if (btf_ptr_has_type_tag(btf, mtype, "nullable"))
+ tmp_flag |= PTR_MAYBE_NULL;
+
+ /*
+ * An __arena member of a kernel struct holds the kernel
+ * address of arena memory, which the verifier turns into
+ * an arena pointer on load. Program BTF is not affected:
+ * there the tag is what the compiler emits for the arena
+ * address space. An offset into a union resolves to its
+ * first member, so a program reading another member there
+ * would get an arena pointer it did not ask for; the walk
+ * marked such a union untrusted.
+ */
+ if (btf_is_kernel(btf) && btf_type_is_arena_ptr(btf, mtype)) {
+ if (*flag & PTR_UNTRUSTED) {
+ bpf_log(log,
+ "arena pointer member %s of %s is inside a union\n",
+ mname, tname);
+ return -EACCES;
+ }
+ *next_btf_id = id;
+ *flag |= tmp_flag & PTR_MAYBE_NULL;
+ if (field_name)
+ *field_name = mname;
+ return WALK_PTR_ARENA;
+ }
+
if (btf_type_is_struct(stype)) {
*next_btf_id = id;
*flag |= tmp_flag;
@@ -7431,15 +8044,29 @@ error:
int btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
- int off, int size, enum bpf_access_type atype __maybe_unused,
+ int _off, int size, enum bpf_access_type atype __maybe_unused,
u32 *next_btf_id, enum bpf_type_flag *flag,
const char **field_name)
{
const struct btf *btf = reg->btf;
enum bpf_type_flag tmp_flag = 0;
+ struct array_access arrays[MAX_ARRAYS_WALK];
+ const struct btf_member *member;
const struct btf_type *t;
+ u32 i, arrays_cnt = 0;
u32 id = reg->btf_id;
- int err;
+ s64 min_off, max_off, flex_off;
+ int err, off, ret;
+
+ if (check_add_overflow(reg_smin(reg), (s64)_off, &min_off) ||
+ check_add_overflow(reg_smax(reg), (s64)_off, &max_off) ||
+ min_off != (int)min_off) {
+ bpf_log(log,
+ "offset computation overflows: register offset range is [%lld, %lld], instruction offset is %d\n",
+ reg_smin(reg), reg_smax(reg), _off);
+ return -EINVAL;
+ }
+ off = min_off;
while (type_is_alloc(reg->type)) {
struct btf_struct_meta *meta;
@@ -7466,26 +8093,35 @@ 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, !type_is_alloc(reg->type));
-
+ field_name, !type_is_alloc(reg->type), arrays, &arrays_cnt);
switch (err) {
case WALK_PTR:
/* For local types, the destination register cannot
* become a pointer again.
*/
- if (type_is_alloc(reg->type))
- return SCALAR_VALUE;
+ if (type_is_alloc(reg->type)) {
+ ret = SCALAR_VALUE;
+ goto check_variable_offset;
+ }
/* If we found the pointer or scalar on t+off,
* we're done.
*/
*next_btf_id = id;
*flag = tmp_flag;
- return PTR_TO_BTF_ID;
+ ret = PTR_TO_BTF_ID;
+ goto check_variable_offset;
case WALK_PTR_UNTRUSTED:
*flag = MEM_RDONLY | PTR_UNTRUSTED;
- return PTR_TO_MEM;
+ ret = PTR_TO_MEM;
+ goto check_variable_offset;
+ case WALK_PTR_ARENA:
+ *next_btf_id = id;
+ *flag = tmp_flag;
+ ret = PTR_TO_ARENA;
+ goto check_variable_offset;
case WALK_SCALAR:
- return SCALAR_VALUE;
+ ret = SCALAR_VALUE;
+ goto check_variable_offset;
case WALK_STRUCT:
/* We found nested struct, so continue the search
* by diving in it. At this point the offset is
@@ -7505,6 +8141,55 @@ int btf_struct_access(struct bpf_verifier_log *log,
} while (t);
return -EINVAL;
+
+check_variable_offset:
+ if (min_off == max_off)
+ return ret;
+
+ /* Find an offset at which access would go to a flexible array tail (if any). */
+ t = btf_type_skip_modifiers(btf, reg->btf_id, NULL);
+ member = find_flex_member(btf, t);
+ flex_off = member ? __btf_member_bit_offset(t, member) / 8 : S64_MAX;
+
+ /*
+ * If this is a varying offset access, the step recorded within a register
+ * should correspond to one of the arrays visited while walking.
+ */
+ for (i = 0; i < arrays_cnt; i++) {
+ s64 array_start, array_end, access_end;
+ u32 asize, esize, elem_id;
+
+ /*
+ * 'arrays' records top-level arrays only, use __btf_resolve_size()
+ * to get flattened representation.
+ */
+ t = btf_type_by_id(btf, arrays[i].id);
+ if (IS_ERR(__btf_resolve_size(btf, t, &asize, NULL, &elem_id, NULL, NULL)) ||
+ IS_ERR(btf_resolve_size(btf, btf_type_by_id(btf, elem_id), &esize)))
+ continue;
+ /* Make sure every accessed offset lands on the same field of some element. */
+ if (esize == 0 || reg->step % esize != 0)
+ continue;
+
+ array_start = min_off - arrays[i].off;
+ /* An array within the flexible tail has no upper bound to exceed */
+ if (array_start >= flex_off && btf_type_is_array(t) &&
+ btf_type_array(t)->nelems == 0)
+ return ret;
+
+ /* the 'size' bytes read at max_off must stay within the array */
+ if (check_add_overflow(array_start, (s64)asize, &array_end) ||
+ check_add_overflow(max_off, (s64)size, &access_end) ||
+ access_end > array_end)
+ continue;
+ return ret;
+ }
+
+ bpf_log(log,
+ "invalid variable offset access into struct %s: offsets [%lld, %lld] size %d step %d do not match any array\n",
+ btf_name_by_offset(btf, btf_type_by_id(btf, reg->btf_id)->name_off),
+ min_off, max_off, size, reg->step);
+ return -EINVAL;
}
/* Check that two BTF types, each specified as an BTF object + id, are exactly
@@ -7545,7 +8230,7 @@ again:
if (!type)
return false;
err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL,
- walk_flex_arrays);
+ walk_flex_arrays, NULL, NULL);
if (err != WALK_STRUCT)
return false;
@@ -7601,6 +8286,9 @@ static u8 __get_arg_fmodel_flags(const struct btf *btf,
{
u8 flags = __get_type_fmodel_flags(t);
+ if (btf_func_arg_align(btf, t) > sizeof(u64))
+ flags |= BTF_FMODEL_ALIGN16_ARG;
+
if (btf_param_match_suffix(btf, arg, "__arena__nullable"))
flags |= BTF_FMODEL_ARENA_ARG | BTF_FMODEL_NULLABLE_ARG;
else if (btf_param_match_suffix(btf, arg, "__arena"))
@@ -7644,7 +8332,7 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
return -EINVAL;
}
ret = __get_type_size(btf, func->type, &t);
- if (ret < 0 || btf_type_is_struct(t)) {
+ if (ret < 0 || ret > 16) {
bpf_log(log,
"The function %s return type %s is unsupported.\n",
tname, btf_type_str(t));
@@ -7737,6 +8425,12 @@ static int btf_check_func_type_match(struct bpf_verifier_log *log,
btf_type_str(t2), fn2);
return -EINVAL;
}
+ if (btf_type_has_size(t1) && (t1->size > 8 || t2->size > 8)) {
+ bpf_log(log,
+ "Return type of %s() has size %u and of %s() size %u, and a size above 8 bytes cannot be replaced\n",
+ fn1, t1->size, fn2, t2->size);
+ return -EINVAL;
+ }
for (i = 0; i < nargs1; i++) {
t1 = btf_type_skip_modifiers(btf1, args1[i].type, NULL);
@@ -7981,51 +8675,19 @@ static int btf_scan_decl_tags(struct bpf_verifier_env *env,
return 0;
}
-static int btf_scan_type_tags(struct bpf_verifier_env *env,
- const struct btf *btf, u32 type_id,
- u32 *tags)
+static void btf_scan_type_tags(const struct btf *btf, u32 type_id, u32 *tags)
{
- static const struct btf_type_tag_match func_type_tags[] = {
- { "arena", ARG_TAG_ARENA },
- };
- struct btf_type_tag_walk_ctx ctx;
- const struct btf_type *t;
- int err;
-
/* Find the first pointer type in the chain. */
- t = btf_type_skip_modifiers(btf, type_id, NULL);
-
- /*
- * We currently reject type tags on non-pointer types,
- * which neither LLVM nor GCC support anyway.
- */
- if (!t || !btf_type_is_ptr(t))
- return 0;
+ const struct btf_type *t = btf_type_skip_modifiers(btf, type_id, NULL);
- ctx.t = t;
- err = btf_type_tag_walk(btf, &ctx, func_type_tags,
- ARRAY_SIZE(func_type_tags));
- if (err) {
- bpf_log(&env->log,
- "function signature member has multiple type tags\n");
- return err;
- }
- *tags |= ctx.res;
-
- return 0;
+ if (btf_type_is_arena_ptr(btf, t))
+ *tags |= ARG_TAG_ARENA;
}
/* Check whether the type is a valid return type. */
static int btf_validate_return_type(struct bpf_verifier_env *env, struct btf *btf,
- const struct btf_type *t, int subprog)
+ const struct btf_type *t, int subprog, bool is_global)
{
- u32 tags = 0;
- int err;
-
- err = btf_scan_type_tags(env, btf, t->type, &tags);
- if (err)
- return err;
-
t = btf_type_skip_modifiers(btf, t->type, NULL);
/*
@@ -8033,16 +8695,62 @@ static int btf_validate_return_type(struct bpf_verifier_env *env, struct btf *bt
* General arena variables are not allowed, since it makes no sense to return by value
* a variable that's on the heap in the first place.
*/
- if (subprog && (tags & ARG_TAG_ARENA) && btf_type_is_ptr(t))
+ if (subprog && btf_type_is_arena_ptr(btf, t))
return 0;
/* We always accept void or scalars. */
if (btf_type_is_void(t) || btf_type_is_int(t) || btf_is_any_enum(t))
return 0;
+ if (btf_type_is_struct(t) && t->size <= 16) {
+ /*
+ * A global function's caller models the return as an opaque
+ * scalar pair, so a pointer member would be laundered into a
+ * scalar and escape provenance and reference tracking. Only
+ * scalars and arena pointers are allowed: an arena pointer has
+ * no provenance to lose, since a program may already derive one
+ * from any scalar with addr_space_cast(), which confines the
+ * result to the arena. The main program is the exception: it
+ * returns to the kernel, which has no arena to cast the address
+ * back into. A local function is verified inline, so its caller
+ * receives the real register state and any member is fine.
+ */
+ bool local_func = subprog && !is_global;
+ u32 member_kinds = BTF_MEMBER_SCALAR;
+
+ if (subprog)
+ member_kinds |= BTF_MEMBER_ARENA_PTR;
+
+ if (local_func || btf_struct_is_composed_of(env, btf, t, member_kinds))
+ return 0;
+ }
+
return -EOPNOTSUPP;
}
+static int btf_check_arg_slots(struct bpf_verifier_log *log, const char *tname,
+ bool is_global, u32 slot_cnt,
+ struct bpf_subprog_info *sub)
+{
+ if (slot_cnt <= MAX_BPF_FUNC_REG_ARGS)
+ return 0;
+
+ if (is_global) {
+ bpf_log(log,
+ "global function %s() needs %d > %d argument slots, "
+ "stack args not supported\n",
+ tname, slot_cnt, MAX_BPF_FUNC_REG_ARGS);
+ return -EINVAL;
+ }
+ if (!bpf_jit_supports_stack_args()) {
+ bpf_log(log, "JIT does not support function %s() with %d argument slots\n",
+ tname, slot_cnt);
+ return -EFAULT;
+ }
+ sub->stack_arg_cnt = slot_cnt - MAX_BPF_FUNC_REG_ARGS;
+ return 0;
+}
+
/* Process BTF of a function to produce high-level expectation of function
* arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information
* is cached in subprog info for reuse.
@@ -8062,7 +8770,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
const struct btf_param *args;
const struct btf_type *t, *ref_t, *fn_t;
int err;
- u32 i, nargs, btf_id;
+ u32 i, slots_used, nargs, btf_id;
const char *tname;
if (sub->args_cached)
@@ -8106,34 +8814,29 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
}
args = (const struct btf_param *)(t + 1);
nargs = btf_type_vlen(t);
- sub->arg_cnt = nargs;
+ sub->arg_slot_cnt = nargs;
if (nargs > MAX_BPF_FUNC_ARGS) {
bpf_log(log, "kernel supports at most %d parameters, function %s has %d\n",
MAX_BPF_FUNC_ARGS, tname, nargs);
return -EFAULT;
}
- if (nargs > MAX_BPF_FUNC_REG_ARGS) {
- if (!bpf_jit_supports_stack_args()) {
- bpf_log(log, "JIT does not support function %s() with %d args\n",
- tname, nargs);
- return -EFAULT;
- }
- sub->stack_arg_cnt = nargs - MAX_BPF_FUNC_REG_ARGS;
- }
-
- if (is_global && nargs > MAX_BPF_FUNC_REG_ARGS) {
- bpf_log(log, "global function %s has %d > %d args, stack args not supported\n",
- tname, nargs, MAX_BPF_FUNC_REG_ARGS);
- return -EINVAL;
- }
+ err = btf_check_arg_slots(log, tname, is_global, nargs, sub);
+ if (err)
+ return err;
- err = btf_validate_return_type(env, btf, t, subprog);
+ err = btf_validate_return_type(env, btf, t, subprog, is_global);
if (err) {
if (is_global) {
+ /* Only a subprogram may return arena pointers. */
+ const char *supported = subprog ?
+ "void, scalar, arena pointer, or a struct/union of "
+ "scalars and arena pointers" :
+ "void, scalar, or a scalar-only struct/union";
+
bpf_log(log,
- "Global function %s() return value not void or scalar. "
- "Only those are supported.\n",
- tname);
+ "Global function %s() has unsupported return type. "
+ "Only %s up to 16 bytes is supported.\n",
+ tname, supported);
}
return err;
}
@@ -8141,15 +8844,17 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
/* Convert BTF function arguments into verifier types.
* Only PTR_TO_CTX and SCALAR are supported atm.
*/
- for (i = 0; i < nargs; i++) {
+ for (i = 0, slots_used = 0; i < nargs; i++) {
u32 tags = 0;
+
+ if (slots_used >= MAX_BPF_FUNC_ARGS)
+ goto too_many_slots;
+
err = btf_scan_decl_tags(env, btf, fn_t, i, is_global, &tags);
if (err)
return err;
- err = btf_scan_type_tags(env, btf, args[i].type, &tags);
- if (err)
- return err;
+ btf_scan_type_tags(btf, args[i].type, &tags);
t = btf_type_by_id(btf, args[i].type);
while (btf_type_is_modifier(t))
@@ -8166,7 +8871,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
btf_validate_prog_ctx_type(log, btf, t, i, prog_type,
prog->expected_attach_type))
return -EINVAL;
- sub->args[i].arg_type = ARG_PTR_TO_CTX;
+ sub->args[slots_used++].arg_type = ARG_PTR_TO_CTX;
continue;
}
if (btf_is_dynptr_ptr(btf, t)) {
@@ -8174,7 +8879,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
bpf_log(log, "arg#%d has invalid combination of tags\n", i);
return -EINVAL;
}
- sub->args[i].arg_type = ARG_PTR_TO_DYNPTR;
+ sub->args[slots_used++].arg_type = ARG_PTR_TO_DYNPTR;
continue;
}
if (tags & ARG_TAG_TRUSTED) {
@@ -8189,10 +8894,11 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
if (kern_type_id < 0)
return kern_type_id;
- sub->args[i].arg_type = ARG_PTR_TO_BTF_ID | PTR_TRUSTED;
+ sub->args[slots_used].arg_type = ARG_PTR_TO_BTF_ID | PTR_TRUSTED;
if (tags & ARG_TAG_NULLABLE)
- sub->args[i].arg_type |= PTR_MAYBE_NULL;
- sub->args[i].btf_id = kern_type_id;
+ sub->args[slots_used].arg_type |= PTR_MAYBE_NULL;
+ sub->args[slots_used].btf_id = kern_type_id;
+ slots_used++;
continue;
}
if (tags & ARG_TAG_UNTRUSTED) {
@@ -8206,8 +8912,10 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
ref_t = btf_type_skip_modifiers(btf, t->type, NULL);
if (btf_type_is_void(ref_t) || btf_type_is_primitive(ref_t)) {
- sub->args[i].arg_type = ARG_PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED;
- sub->args[i].mem_size = 0;
+ sub->args[slots_used].arg_type = ARG_PTR_TO_MEM | MEM_RDONLY |
+ PTR_UNTRUSTED;
+ sub->args[slots_used].mem_size = 0;
+ slots_used++;
continue;
}
@@ -8223,8 +8931,9 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
i, btf_type_str(ref_t), tname);
return -EINVAL;
}
- sub->args[i].arg_type = ARG_PTR_TO_BTF_ID | PTR_UNTRUSTED;
- sub->args[i].btf_id = kern_type_id;
+ sub->args[slots_used].arg_type = ARG_PTR_TO_BTF_ID | PTR_UNTRUSTED;
+ sub->args[slots_used].btf_id = kern_type_id;
+ slots_used++;
continue;
}
if (tags & ARG_TAG_ARENA) {
@@ -8232,7 +8941,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
bpf_log(log, "arg#%d arena cannot be combined with any other tags\n", i);
return -EINVAL;
}
- sub->args[i].arg_type = ARG_PTR_TO_ARENA;
+ sub->args[slots_used++].arg_type = ARG_PTR_TO_ARENA;
continue;
}
if (is_global) { /* generic user data pointer */
@@ -8252,10 +8961,11 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
return -EINVAL;
}
- sub->args[i].arg_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL;
+ sub->args[slots_used].arg_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL;
if (tags & ARG_TAG_NONNULL)
- sub->args[i].arg_type &= ~PTR_MAYBE_NULL;
- sub->args[i].mem_size = mem_size;
+ sub->args[slots_used].arg_type &= ~PTR_MAYBE_NULL;
+ sub->args[slots_used].mem_size = mem_size;
+ slots_used++;
continue;
}
@@ -8264,8 +8974,32 @@ skip_pointer:
bpf_log(log, "arg#%d has pointer tag, but is not a pointer type\n", i);
return -EINVAL;
}
- if (btf_type_is_int(t) || btf_is_any_enum(t)) {
- sub->args[i].arg_type = ARG_ANYTHING;
+ if (btf_type_is_int(t) || btf_is_any_enum(t) || btf_type_is_struct(t)) {
+ u32 nslots;
+
+ if (!t->size || t->size > 2 * BPF_REG_SIZE) {
+ if (!is_global)
+ return -EINVAL;
+ bpf_log(log,
+ "Arg#%d type %s in %s() has size %u, only 1 to %d bytes "
+ "can be passed by value\n",
+ i, btf_type_str(t), tname, t->size, 2 * BPF_REG_SIZE);
+ return -EINVAL;
+ }
+ if (btf_type_is_struct(t) &&
+ !btf_struct_is_composed_of(env, btf, t, BTF_MEMBER_SCALAR)) {
+ if (!is_global)
+ return -EINVAL;
+ bpf_log(log, "Arg#%d type %s in %s() is not composed of scalars\n",
+ i, btf_type_str(t), tname);
+ return -EINVAL;
+ }
+
+ nslots = (t->size + BPF_REG_SIZE - 1) / BPF_REG_SIZE;
+ if (slots_used + nslots > MAX_BPF_FUNC_ARGS)
+ goto too_many_slots;
+ while (nslots--)
+ sub->args[slots_used++].arg_type = ARG_SCALAR;
continue;
}
if (!is_global)
@@ -8275,9 +9009,21 @@ skip_pointer:
return -EINVAL;
}
+ err = btf_check_arg_slots(log, tname, is_global, slots_used, sub);
+ if (err)
+ return err;
+ sub->arg_slot_cnt = slots_used;
+
sub->args_cached = true;
return 0;
+
+too_many_slots:
+ if (!is_global)
+ return -EINVAL;
+ bpf_log(log, "Arguments of %s() need more than %d argument slots\n",
+ tname, MAX_BPF_FUNC_ARGS);
+ return -EINVAL;
}
static void btf_type_show(const struct btf *btf, u32 type_id, void *obj,
@@ -8552,6 +9298,8 @@ struct btf_module {
struct module *module;
struct btf *btf;
struct bin_attribute *sysfs_attr;
+ struct bin_attribute *sysfs_inline_attr;
+ void *btf_inline_data;
int flags;
};
@@ -8560,28 +9308,149 @@ static DEFINE_MUTEX(btf_module_mutex);
static void purge_cand_cache(struct btf *btf);
+#if IS_MODULE(CONFIG_DEBUG_INFO_BTF_INLINE)
+extern char __start_BTF_inline_link[];
+extern char __stop_BTF_inline_link[];
+
+static struct bin_attribute *vmlinux_inline_attr;
+static const struct btf_link *vmlinux_inline_link;
+
+static const struct btf_link *btf_parse_link(void *start, unsigned int size)
+{
+ const struct btf_link *link = start;
+ unsigned int module_name_len;
+
+ if (!link || size != sizeof(*link) || link->btf_size == 0)
+ return NULL;
+
+ module_name_len = strnlen(link->module_name, sizeof(link->module_name));
+ if (!module_name_len || module_name_len == sizeof(link->module_name) ||
+ memchr_inv(link->module_name + module_name_len + 1, 0,
+ sizeof(link->module_name) - module_name_len - 1))
+ return NULL;
+ return link;
+}
+
+static bool btf_verify_link(const struct btf_link *link, const struct module *mod,
+ const void *data, size_t data_size)
+{
+ u8 actual_digest[BTF_LINK_SHA256_LEN];
+
+ if (!link || data_size != link->btf_size) {
+ pr_warn(" BTF link from module [%s] missing or has unexpected size\n", mod->name);
+ return false;
+ }
+ sha256(data, data_size, actual_digest);
+ if (memcmp(actual_digest, link->sha256, sizeof(actual_digest))) {
+ pr_warn("BTF link from module [%s] has unexpected digest\n", mod->name);
+ return false;
+ }
+
+ return true;
+}
+
+static void btf_vmlinux_inline_lazy_init(void)
+{
+ vmlinux_inline_link = btf_parse_link(__start_BTF_inline_link,
+ __stop_BTF_inline_link -
+ __start_BTF_inline_link);
+ if (!vmlinux_inline_link) {
+ pr_warn("invalid vmlinux inline BTF link\n");
+ return;
+ }
+ vmlinux_inline_attr = sysfs_btf_add("vmlinux.inline", NULL,
+ vmlinux_inline_link->btf_size, true,
+ vmlinux_inline_link->module_name);
+ if (IS_ERR(vmlinux_inline_attr)) {
+ pr_warn("failed to register vmlinux inline BTF in sysfs: %ld\n",
+ PTR_ERR(vmlinux_inline_attr));
+ vmlinux_inline_attr = NULL;
+ }
+}
+
+static bool btf_module_is_vmlinux_inline(const struct module *mod)
+{
+ return vmlinux_inline_link && !strcmp(mod->name, vmlinux_inline_link->module_name);
+}
+
+static int btf_module_handle_vmlinux_inline(const struct module *mod)
+{
+ void *data;
+
+ if (!mod->btf_inline_data ||
+ mod->btf_inline_data_size != vmlinux_inline_link->btf_size)
+ return -EINVAL;
+
+ data = vmalloc_user(mod->btf_inline_data_size);
+ if (!data)
+ return -ENOMEM;
+ memcpy(data, mod->btf_inline_data, mod->btf_inline_data_size);
+
+ if (!btf_verify_link(vmlinux_inline_link, mod, data, mod->btf_inline_data_size) ||
+ !sysfs_btf_update(vmlinux_inline_attr, data)) {
+ kvfree(data);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#else
+
+static bool btf_module_is_vmlinux_inline(const struct module *mod)
+{
+ return false;
+}
+
+static int btf_module_handle_vmlinux_inline(const struct module *mod)
+{
+ return 0;
+}
+
+static void btf_vmlinux_inline_lazy_init(void)
+{
+}
+
+#endif /* IS_MODULE(CONFIG_DEBUG_INFO_BTF_INLINE) */
+
static int btf_module_notify(struct notifier_block *nb, unsigned long op,
void *module)
{
struct btf_module *btf_mod, *tmp;
struct module *mod = module;
+ struct bin_attribute *attr;
struct btf *btf;
+ void *inline_data = NULL, *relocated_inline_data = NULL;
+ unsigned int inline_data_size = 0;
int err = 0;
- if (mod->btf_data_size == 0 ||
- (op != MODULE_STATE_COMING && op != MODULE_STATE_LIVE &&
- op != MODULE_STATE_GOING))
+ if (op != MODULE_STATE_COMING && op != MODULE_STATE_LIVE &&
+ op != MODULE_STATE_GOING)
goto out;
switch (op) {
case MODULE_STATE_COMING:
+ if (btf_module_is_vmlinux_inline(mod)) {
+ err = btf_module_handle_vmlinux_inline(mod);
+ break;
+ }
+
+#if IS_ENABLED(CONFIG_DEBUG_INFO_BTF_INLINE)
+ if (mod->btf_inline_data_size) {
+ inline_data = mod->btf_inline_data;
+ inline_data_size = mod->btf_inline_data_size;
+ }
+#endif
+ if (!mod->btf_data_size)
+ break;
btf_mod = kzalloc_obj(*btf_mod);
if (!btf_mod) {
err = -ENOMEM;
goto out;
}
btf = btf_parse_module(mod->name, mod->btf_data, mod->btf_data_size,
- mod->btf_base_data, mod->btf_base_data_size);
+ mod->btf_base_data, mod->btf_base_data_size,
+ inline_data, inline_data_size,
+ &relocated_inline_data);
if (IS_ERR(btf)) {
kfree(btf_mod);
if (!IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH)) {
@@ -8596,6 +9465,7 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
err = btf_alloc_id(btf);
if (err) {
btf_free(btf);
+ kvfree(relocated_inline_data);
kfree(btf_mod);
goto out;
}
@@ -8604,37 +9474,35 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
mutex_lock(&btf_module_mutex);
btf_mod->module = module;
btf_mod->btf = btf;
+ btf_mod->btf_inline_data = relocated_inline_data;
list_add(&btf_mod->list, &btf_modules);
mutex_unlock(&btf_module_mutex);
- if (IS_ENABLED(CONFIG_SYSFS)) {
- struct bin_attribute *attr;
-
- attr = kzalloc_obj(*attr);
- if (!attr)
- goto out;
+ attr = sysfs_btf_add(btf->name, btf->data, btf->data_size, false, NULL);
+ if (IS_ERR(attr)) {
+ err = 0;
+ goto out;
+ }
+ btf_mod->sysfs_attr = attr;
- sysfs_bin_attr_init(attr);
- attr->attr.name = btf->name;
- attr->attr.mode = 0444;
- attr->size = btf->data_size;
- attr->private = btf->data;
- attr->read = sysfs_bin_attr_simple_read;
+ if (relocated_inline_data) {
+ char name[MODULE_NAME_LEN + sizeof(".inline")];
- err = sysfs_create_bin_file(btf_kobj, attr);
- if (err) {
- pr_warn("failed to register module [%s] BTF in sysfs: %d\n",
- mod->name, err);
- kfree(attr);
+ snprintf(name, sizeof(name), "%s.inline", mod->name);
+ attr = sysfs_btf_add(name, relocated_inline_data,
+ inline_data_size, false, NULL);
+ if (IS_ERR(attr)) {
+ kvfree(relocated_inline_data);
+ btf_mod->btf_inline_data = NULL;
err = 0;
- goto out;
+ break;
}
-
- btf_mod->sysfs_attr = attr;
+ btf_mod->sysfs_inline_attr = attr;
}
-
break;
case MODULE_STATE_LIVE:
+ if (!mod->btf_data_size)
+ break;
mutex_lock(&btf_module_mutex);
list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
if (btf_mod->module != module)
@@ -8646,6 +9514,8 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
mutex_unlock(&btf_module_mutex);
break;
case MODULE_STATE_GOING:
+ if (!mod->btf_data_size)
+ break;
mutex_lock(&btf_module_mutex);
list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
if (btf_mod->module != module)
@@ -8660,10 +9530,12 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
btf_free_id(btf_mod->btf);
list_del(&btf_mod->list);
if (btf_mod->sysfs_attr)
- sysfs_remove_bin_file(btf_kobj, btf_mod->sysfs_attr);
+ sysfs_btf_remove(btf_mod->sysfs_attr);
+ if (btf_mod->sysfs_inline_attr)
+ sysfs_btf_remove(btf_mod->sysfs_inline_attr);
+ kvfree(btf_mod->btf_inline_data);
purge_cand_cache(btf_mod->btf);
btf_put(btf_mod->btf);
- kfree(btf_mod->sysfs_attr);
kfree(btf_mod);
break;
}
@@ -8680,6 +9552,7 @@ static struct notifier_block btf_module_nb = {
static int __init btf_module_init(void)
{
+ btf_vmlinux_inline_lazy_init();
register_module_notifier(&btf_module_nb);
return 0;
}
@@ -9953,6 +10826,7 @@ btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops,
struct bpf_verifier_log *log)
{
struct btf_struct_ops_tab *tab, *new_tab;
+ int cgroup_atype;
int i, err;
tab = btf->struct_ops_tab;
@@ -9964,8 +10838,10 @@ btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops,
btf->struct_ops_tab = tab;
}
+ cgroup_atype = st_ops->cgroup_atype;
for (i = 0; i < tab->cnt; i++)
- if (tab->ops[i].st_ops == st_ops)
+ if (tab->ops[i].st_ops == st_ops ||
+ (cgroup_atype && cgroup_atype == tab->ops[i].st_ops->cgroup_atype))
return -EEXIST;
if (tab->cnt == tab->capacity) {
@@ -9985,6 +10861,31 @@ btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops,
if (err)
return err;
+ if (cgroup_atype) {
+ /*
+ * Cgroup struct_ops callers hold the RCU read lock around the
+ * entire trampoline call, including its trailing instructions.
+ * A regular RCU grace period therefore protects both the kdata
+ * and the trampoline image, so a tasks RCU grace period is not
+ * needed.
+ */
+ if (!cgroup_bpf_is_struct_ops_atype(cgroup_atype) ||
+ st_ops->reg || st_ops->unreg || st_ops->free_after_tasks_rcu_gp) {
+ bpf_struct_ops_desc_release(&tab->ops[btf->struct_ops_tab->cnt]);
+ return -EINVAL;
+ }
+
+ /*
+ * There is no need to unregister from the cgroup when btf_free()
+ * runs. No struct_ops map or cgroup link can be created once its
+ * BTF is gone.
+ */
+ cgroup_bpf_struct_ops_register(cgroup_atype,
+ tab->ops[btf->struct_ops_tab->cnt].type_id,
+ st_ops->cfi_stubs,
+ st_ops->free_after_mult_rcu_gp);
+ }
+
btf->struct_ops_tab->cnt++;
return 0;
diff --git a/kernel/bpf/btf_vmlinux_inline.c b/kernel/bpf/btf_vmlinux_inline.c
new file mode 100644
index 000000000000..d5c599d9f729
--- /dev/null
+++ b/kernel/bpf/btf_vmlinux_inline.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026, Oracle and/or its affiliates. */
+/*
+ * Provide kernel BTF inline function information for use by BPF tools.
+ */
+#include <linux/btf.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#if IS_BUILTIN(CONFIG_DEBUG_INFO_BTF_INLINE)
+extern char __start_BTF_inline[];
+extern char __stop_BTF_inline[];
+#endif
+
+static int __init btf_vmlinux_inline_init(void)
+{
+#if IS_BUILTIN(CONFIG_DEBUG_INFO_BTF_INLINE)
+ size_t data_size = __stop_BTF_inline - __start_BTF_inline;
+
+ if (data_size)
+ sysfs_btf_add("vmlinux.inline", __start_BTF_inline, data_size, true, NULL);
+#endif
+ return 0;
+}
+subsys_initcall(btf_vmlinux_inline_init);
+
+MODULE_DESCRIPTION("BTF inline information for vmlinux");
+MODULE_LICENSE("GPL");
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 842c7d1eabcc..2ee950e9ac82 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -9,6 +9,8 @@
#define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args)
+#define BPF_MAX_GOTOX_EDGES BPF_COMPLEXITY_LIMIT_INSNS
+
/* non-recursive DFS pseudo code
* 1 procedure DFS-iterative(G,v):
* 2 label v as discovered
@@ -284,15 +286,17 @@ err_free:
}
/*
- * Find and collect all maps which fit in the subprog. Return the result as one
- * combined jump table in jt->items (allocated with kvcalloc)
+ * Collect the jump table of every subprogram that has one, as the combined
+ * table of all maps whose targets land inside that subprogram. All gotox
+ * instructions of a subprogram share the same table, so this is done in a
+ * single pass over the maps rather than once per gotox.
*/
-static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,
- int subprog_start, int subprog_end)
+static int __compute_subprog_jts(struct bpf_verifier_env *env)
{
- struct bpf_iarray *jt = NULL;
+ struct bpf_subprog_info *subprog;
+ struct bpf_iarray *jt, *jt_cur;
struct bpf_map *map;
- struct bpf_iarray *jt_cur;
+ u32 old_cnt;
int i;
for (i = 0; i < env->insn_array_map_cnt; i++) {
@@ -303,73 +307,100 @@ static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,
map = env->insn_array_maps[i];
jt_cur = jt_from_map(map);
- if (IS_ERR(jt_cur)) {
- kvfree(jt);
- return jt_cur;
+ if (IS_ERR(jt_cur))
+ return PTR_ERR(jt_cur);
+
+ subprog = bpf_find_containing_subprog(env, jt_cur->items[0]);
+ if (verifier_bug_if(!subprog, env, "no subprog contains insn %u of an insn array map",
+ jt_cur->items[0])) {
+ kvfree(jt_cur);
+ return -EFAULT;
+ }
+ if (jt_cur->items[jt_cur->cnt - 1] >= (subprog + 1)->start) {
+ verbose(env, "jump table of subprog starting at %u spans multiple subprogs, map id=%d\n",
+ subprog->start, map->id);
+ bpf_diag_program_structure(
+ env, subprog->start, "jump table spans subprograms",
+ "Keep every entry of a jump table inside one subprogram.",
+ "The jump table in map id %d for the subprogram that starts at instruction %u reaches past its end at instruction %u.",
+ map->id, subprog->start, (subprog + 1)->start);
+ kvfree(jt_cur);
+ return -EINVAL;
}
- /*
- * This is enough to check one element. The full table is
- * checked to fit inside the subprog later in create_jt()
- */
- if (jt_cur->items[0] >= subprog_start && jt_cur->items[0] < subprog_end) {
- u32 old_cnt = jt ? jt->cnt : 0;
- jt = bpf_iarray_realloc(jt, old_cnt + jt_cur->cnt);
- if (!jt) {
- kvfree(jt_cur);
- return ERR_PTR(-ENOMEM);
- }
- memcpy(jt->items + old_cnt, jt_cur->items, jt_cur->cnt << 2);
+ old_cnt = subprog->jt ? subprog->jt->cnt : 0;
+ jt = bpf_iarray_realloc(subprog->jt, old_cnt + jt_cur->cnt);
+ if (!jt) {
+ subprog->jt = NULL;
+ kvfree(jt_cur);
+ return -ENOMEM;
}
+ memcpy(jt->items + old_cnt, jt_cur->items, jt_cur->cnt << 2);
+ subprog->jt = jt;
kvfree(jt_cur);
}
- if (!jt) {
- verbose(env, "no jump tables found for subprog starting at %u\n", subprog_start);
- bpf_diag_program_structure(
- env, subprog_start, "missing jump table",
- "Make sure subprograms containing gotox instructions are accompanied by jump tables referencing these subprograms.",
- "No jump table was found for the subprogram that starts at instruction %u.",
- subprog_start);
- return ERR_PTR(-EINVAL);
+ for (i = 0; i < env->subprog_cnt; i++) {
+ jt = env->subprog_info[i].jt;
+ if (jt)
+ jt->cnt = sort_insn_array_uniq(jt->items, jt->cnt);
}
- jt->cnt = sort_insn_array_uniq(jt->items, jt->cnt);
- return jt;
+ env->cfg.subprog_jts_ready = true;
+ return 0;
+}
+
+/* Leave nothing behind on failure. */
+static int compute_subprog_jts(struct bpf_verifier_env *env)
+{
+ int err;
+
+ err = __compute_subprog_jts(env);
+ if (err)
+ bpf_free_subprog_jts(env);
+ return err;
+}
+
+void bpf_free_subprog_jts(struct bpf_verifier_env *env)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(env->subprog_info); i++) {
+ kvfree(env->subprog_info[i].jt);
+ env->subprog_info[i].jt = NULL;
+ }
+ env->cfg.subprog_jts_ready = false;
}
static struct bpf_iarray *
-create_jt(int t, struct bpf_verifier_env *env)
+subprog_jt(int t, struct bpf_verifier_env *env)
{
struct bpf_subprog_info *subprog;
- int subprog_start, subprog_end;
- struct bpf_iarray *jt;
- int i;
+ int subprog_start, err;
+
+ if (!env->cfg.subprog_jts_ready) {
+ err = compute_subprog_jts(env);
+ if (err)
+ return ERR_PTR(err);
+ }
subprog = bpf_find_containing_subprog(env, t);
+ if (verifier_bug_if(!subprog, env, "no subprog contains insn %d", t))
+ return ERR_PTR(-EFAULT);
subprog_start = subprog->start;
- subprog_end = (subprog + 1)->start;
- jt = jt_from_subprog(env, subprog_start, subprog_end);
- if (IS_ERR(jt))
- return jt;
- /* Check that the every element of the jump table fits within the given subprogram */
- for (i = 0; i < jt->cnt; i++) {
- if (jt->items[i] < subprog_start || jt->items[i] >= subprog_end) {
- verbose(env, "jump table for insn %d points outside of the subprog [%u,%u]\n",
- t, subprog_start, subprog_end);
- bpf_diag_program_structure(
- env, t, "jump table target out of range",
- "Keep every jump-table target inside the same subprogram.",
- "The jump table for instruction %d points outside subprogram range [%u,%u).",
- t, subprog_start, subprog_end);
- kvfree(jt);
- return ERR_PTR(-EINVAL);
- }
+ if (!subprog->jt) {
+ verbose(env, "no jump tables found for subprog starting at %u\n", subprog_start);
+ bpf_diag_program_structure(
+ env, t, "missing jump table",
+ "Make sure subprograms containing gotox instructions are accompanied by jump tables referencing these subprograms.",
+ "No jump table was found for the subprogram that starts at instruction %u.",
+ subprog_start);
+ return ERR_PTR(-EINVAL);
}
- return jt;
+ return subprog->jt;
}
/* "conditional jump with N edges" */
@@ -381,27 +412,39 @@ static int visit_gotox_insn(int t, struct bpf_verifier_env *env)
struct bpf_iarray *jt;
int i, w;
- jt = env->insn_aux_data[t].jt;
- if (!jt) {
- jt = create_jt(t, env);
- if (IS_ERR(jt))
- return PTR_ERR(jt);
+ /*
+ * The first visit pushes every target that still needs exploring, and
+ * those are all done by the time this insn is revisited, so there is
+ * nothing left to walk the jump table for.
+ */
+ if (insn_state[t] & BRANCH)
+ return DONE_EXPLORING;
+
+ jt = subprog_jt(t, env);
+ if (IS_ERR(jt))
+ return PTR_ERR(jt);
- env->insn_aux_data[t].jt = jt;
+ insn_state[t] |= BRANCH;
+
+ if (check_add_overflow(env->cfg.gotox_edges, jt->cnt,
+ &env->cfg.gotox_edges) ||
+ env->cfg.gotox_edges > BPF_MAX_GOTOX_EDGES) {
+ verbose(env, "number of indirect jump edges in the program exceeds %u\n",
+ BPF_MAX_GOTOX_EDGES);
+ bpf_diag_program_structure(
+ env, t, "too many indirect jump edges",
+ "Reduce the number of indirect jumps, or the number of distinct targets they can reach.",
+ "The program has more than %u indirect jump edges in total, counted over every gotox instruction.",
+ BPF_MAX_GOTOX_EDGES);
+ return -E2BIG;
}
mark_prune_point(env, t);
for (i = 0; i < jt->cnt; i++) {
w = jt->items[i];
- if (w < 0 || w >= env->prog->len) {
- verbose(env, "indirect jump out of range from insn %d to %d\n", t, w);
- bpf_diag_program_structure(
- env, t, "indirect jump out of range",
- "Keep indirect jump targets inside the program.",
- "Instruction %d can jump indirectly to instruction %d, but the program only contains instructions 0 through %d.",
- t, w, env->prog->len - 1);
- return -EINVAL;
- }
+ if (verifier_bug_if(w < 0 || w >= env->prog->len, env,
+ "indirect jump from insn %d to %d out of range", t, w))
+ return -EFAULT;
mark_jmp_point(env, w);
mark_jump_target(env, w);
@@ -422,27 +465,73 @@ static int visit_gotox_insn(int t, struct bpf_verifier_env *env)
}
/*
- * Instructions that can abnormally return from a subprog (tail_call
- * upon success, ld_{abs,ind} upon load failure) have a hidden exit
- * that the verifier must account for.
+ * Return pointers to functions in the read-only map that ld_imm64 instruction
+ * 't' loads the address of, or of its value, if there are any.
*/
-static int visit_abnormal_return_insn(struct bpf_verifier_env *env, int t)
+static struct bpf_func_ptr *insn_func_ptrs(struct bpf_verifier_env *env, int t, u32 *cnt)
{
- struct bpf_subprog_info *subprog;
- struct bpf_iarray *jt;
+ struct bpf_insn *insn = &env->prog->insnsi[t];
- if (env->insn_aux_data[t].jt)
- return 0;
+ *cnt = 0;
+ if (!env->func_ptr_cnt || !bpf_is_ldimm64(insn))
+ return NULL;
+ if (insn->src_reg != BPF_PSEUDO_MAP_VALUE &&
+ insn->src_reg != BPF_PSEUDO_MAP_IDX_VALUE &&
+ insn->src_reg != BPF_PSEUDO_MAP_FD &&
+ insn->src_reg != BPF_PSEUDO_MAP_IDX)
+ return NULL;
- jt = bpf_iarray_realloc(NULL, 2);
- if (!jt)
- return -ENOMEM;
+ return bpf_map_func_ptrs(env, env->used_maps[env->insn_aux_data[t].map_index], cnt);
+}
- subprog = bpf_find_containing_subprog(env, t);
- jt->items[0] = t + 1;
- jt->items[1] = subprog->exit_idx;
- env->insn_aux_data[t].jt = jt;
- return 0;
+/*
+ * ld_imm64 that loads the address of a map that has pointers to functions
+ * is similar to ld_imm64 with BPF_PSEUDO_FUNC that loads the address of one
+ * function: any of them may be read from the map and called via callx later.
+ * Treat it as a call of all of them.
+ */
+static int visit_func_ptrs_insn(int t, struct bpf_verifier_env *env,
+ struct bpf_func_ptr *ptrs, u32 cnt)
+{
+ int *insn_stack = env->cfg.insn_stack;
+ int *insn_state = env->cfg.insn_state;
+ int ret, w;
+ u32 i;
+
+ ret = push_insn(t, t + 2, FALLTHROUGH, env);
+ if (ret)
+ return ret;
+
+ /*
+ * Explore one function at a time like push_insn() does, so that
+ * DISCOVERED function is a caller of 't' and not the one that waits in
+ * insn_stack[], which merge_callee_effects() relies on.
+ */
+ for (i = 0; i < cnt; i++) {
+ w = ptrs[i].xlated_off;
+ if (insn_state[w])
+ continue;
+
+ if (env->cfg.cur_stack >= env->prog->len)
+ return -E2BIG;
+
+ insn_stack[env->cfg.cur_stack++] = w;
+ insn_state[w] = DISCOVERED;
+ return KEEP_EXPLORING;
+ }
+
+ /* all functions are explored, their effects are known */
+ mark_prune_point(env, t);
+ for (i = 0; i < cnt; i++) {
+ w = ptrs[i].xlated_off;
+ merge_callee_effects(env, t, w);
+
+ /* the same marks as push_insn() leaves on a branch target */
+ mark_prune_point(env, w);
+ mark_jmp_point(env, w);
+ mark_jump_target(env, w);
+ }
+ return DONE_EXPLORING;
}
/* Visits the instruction at index t and returns one of the following:
@@ -453,21 +542,20 @@ static int visit_abnormal_return_insn(struct bpf_verifier_env *env, int t)
static int visit_insn(int t, struct bpf_verifier_env *env)
{
struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t];
+ struct bpf_func_ptr *ptrs;
int ret, off, insn_sz;
+ u32 cnt;
if (bpf_pseudo_func(insn))
return visit_func_call_insn(t, insns, env, true);
+ ptrs = insn_func_ptrs(env, t, &cnt);
+ if (ptrs)
+ return visit_func_ptrs_insn(t, env, ptrs, cnt);
+
/* All non-branch instructions have a single fall-through edge. */
if (BPF_CLASS(insn->code) != BPF_JMP &&
BPF_CLASS(insn->code) != BPF_JMP32) {
- if (BPF_CLASS(insn->code) == BPF_LD &&
- (BPF_MODE(insn->code) == BPF_ABS ||
- BPF_MODE(insn->code) == BPF_IND)) {
- ret = visit_abnormal_return_insn(env, t);
- if (ret)
- return ret;
- }
insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
return push_insn(t, t + insn_sz, FALLTHROUGH, env);
}
@@ -510,13 +598,10 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
*/
if (ret == 0 && fp->might_sleep)
mark_subprog_might_sleep(env, t);
- if (bpf_helper_changes_pkt_data(insn->imm))
+ /* nobody knows yet whether the dynptr is that of an skb */
+ if (bpf_helper_changes_pkt_data(insn->imm) ||
+ insn->imm == BPF_FUNC_dynptr_write)
mark_subprog_changes_pkt_data(env, t);
- if (insn->imm == BPF_FUNC_tail_call) {
- ret = visit_abnormal_return_insn(env, t);
- if (ret)
- return ret;
- }
} else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
struct bpf_call_arg_meta meta;
@@ -684,6 +769,11 @@ err_free:
return ret;
}
+struct dfs_state {
+ u32 traversed:1;
+ u32 next_succ:31;
+};
+
/*
* For each subprogram 'i' fill array env->cfg.insn_subprogram sub-range
* [env->subprog_info[i].postorder_start, env->subprog_info[i+1].postorder_start)
@@ -691,43 +781,52 @@ err_free:
*/
int bpf_compute_postorder(struct bpf_verifier_env *env)
{
- u32 cur_postorder, i, top, stack_sz, s;
- int *stack = NULL, *postorder = NULL, *state = NULL;
- struct bpf_iarray *succ;
+ int *stack = NULL, *postorder = NULL, *postorder_nums = NULL;
+ int subprog_idx, stack_sz, cur, s, cur_postorder, start;
+ struct dfs_state *state = NULL;
+ struct bpf_iarray *succ = NULL;
+ postorder_nums = kvzalloc_objs(int, env->prog->len, GFP_KERNEL_ACCOUNT);
postorder = kvzalloc_objs(int, env->prog->len, GFP_KERNEL_ACCOUNT);
- state = kvzalloc_objs(int, env->prog->len, GFP_KERNEL_ACCOUNT);
stack = kvzalloc_objs(int, env->prog->len, GFP_KERNEL_ACCOUNT);
- if (!postorder || !state || !stack) {
+ state = kvzalloc_objs(struct dfs_state, env->prog->len, GFP_KERNEL_ACCOUNT);
+ if (!postorder_nums || !postorder || !stack || !state) {
+ kvfree(postorder_nums);
kvfree(postorder);
- kvfree(state);
kvfree(stack);
+ kvfree(state);
return -ENOMEM;
}
cur_postorder = 0;
- for (i = 0; i < env->subprog_cnt; i++) {
- env->subprog_info[i].postorder_start = cur_postorder;
- stack[0] = env->subprog_info[i].start;
+ for (subprog_idx = 0; subprog_idx < env->subprog_cnt; subprog_idx++) {
+ start = env->subprog_info[subprog_idx].start;
+ env->subprog_info[subprog_idx].postorder_start = cur_postorder;
+ stack[0] = start;
stack_sz = 1;
+ state[start].traversed = true;
do {
- top = stack[stack_sz - 1];
- state[top] |= DISCOVERED;
- if (state[top] & EXPLORED) {
- postorder[cur_postorder++] = top;
+ cur = stack[stack_sz - 1];
+ succ = bpf_insn_successors(env, cur);
+ if (state[cur].next_succ == succ->cnt) {
+ postorder_nums[cur] = cur_postorder;
+ postorder[cur_postorder] = cur;
+ cur_postorder++;
stack_sz--;
continue;
}
- succ = bpf_insn_successors(env, top);
- for (s = 0; s < succ->cnt; ++s) {
- if (!state[succ->items[s]]) {
- stack[stack_sz++] = succ->items[s];
- state[succ->items[s]] |= DISCOVERED;
- }
+ s = succ->items[state[cur].next_succ];
+ if (!state[s].traversed) {
+ state[s].traversed = true;
+ state[s].next_succ = 0;
+ stack[stack_sz] = s;
+ stack_sz++;
+ continue;
}
- state[top] |= EXPLORED;
+ state[cur].next_succ++;
} while (stack_sz);
}
- env->subprog_info[i].postorder_start = cur_postorder;
+ env->subprog_info[subprog_idx].postorder_start = cur_postorder;
+ env->cfg.postorder_nums = postorder_nums;
env->cfg.insn_postorder = postorder;
env->cfg.cur_postorder = cur_postorder;
kvfree(stack);
@@ -749,7 +848,7 @@ int bpf_compute_scc(struct bpf_verifier_env *env)
struct bpf_insn_aux_data *aux = env->insn_aux_data;
const u32 insn_cnt = env->prog->len;
int stack_sz, dfs_sz, err = 0;
- u32 *stack, *pre, *low, *dfs;
+ u32 *stack, *pre, *low, *dfs, *dfs_pos;
u32 i, j, t, w;
u32 next_preorder_num;
u32 next_scc_id;
@@ -762,13 +861,16 @@ int bpf_compute_scc(struct bpf_verifier_env *env)
* - 'stack' accumulates vertices in DFS order, see invariant comment below;
* - 'pre[t] == p' => preorder number of vertex 't' is 'p';
* - 'low[t] == n' => smallest preorder number of the vertex reachable from 't' is 'n';
- * - 'dfs' DFS traversal stack, used to emulate explicit recursion.
+ * - 'dfs' DFS traversal stack, used to emulate explicit recursion;
+ * - 'dfs_pos[k] == j' => the frame 'dfs[k]' resumes visiting its
+ * successors at index 'j'.
*/
stack = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL_ACCOUNT);
pre = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL_ACCOUNT);
low = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL_ACCOUNT);
dfs = kvcalloc(insn_cnt, sizeof(*dfs), GFP_KERNEL_ACCOUNT);
- if (!stack || !pre || !low || !dfs) {
+ dfs_pos = kvcalloc(insn_cnt, sizeof(*dfs_pos), GFP_KERNEL_ACCOUNT);
+ if (!stack || !pre || !low || !dfs || !dfs_pos) {
err = -ENOMEM;
goto exit;
}
@@ -851,6 +953,7 @@ int bpf_compute_scc(struct bpf_verifier_env *env)
stack_sz = 0;
dfs_sz = 1;
dfs[0] = i;
+ dfs_pos[0] = 0;
dfs_continue:
while (dfs_sz) {
w = dfs[dfs_sz - 1];
@@ -860,13 +963,22 @@ dfs_continue:
next_preorder_num++;
stack[stack_sz++] = w;
}
- /* Visit 'w' successors */
+ /* Visit remaining 'w' successors */
succ = bpf_insn_successors(env, w);
- for (j = 0; j < succ->cnt; ++j) {
+ for (j = dfs_pos[dfs_sz - 1]; j < succ->cnt; ++j) {
if (pre[succ->items[j]]) {
low[w] = min(low[w], low[succ->items[j]]);
} else {
- dfs[dfs_sz++] = succ->items[j];
+ /*
+ * Once DFS for succ->items[j] is complete,
+ * pre[succ->items[j]] is non-zero, hence
+ * resuming at 'j' allows to follow the
+ * low[w] = min(...) update branch above.
+ */
+ dfs_pos[dfs_sz - 1] = j;
+ dfs_pos[dfs_sz] = 0;
+ dfs[dfs_sz] = succ->items[j];
+ dfs_sz++;
goto dfs_continue;
}
}
@@ -916,5 +1028,6 @@ exit:
kvfree(pre);
kvfree(low);
kvfree(dfs);
+ kvfree(dfs_pos);
return err;
}
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 149672c76c49..ce82963bcfc7 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -24,6 +24,34 @@
DEFINE_STATIC_KEY_ARRAY_FALSE(cgroup_bpf_enabled_key, MAX_CGROUP_BPF_ATTACH_TYPE);
EXPORT_SYMBOL(cgroup_bpf_enabled_key);
+struct cgroup_struct_ops {
+ u32 type_id;
+ void *cfi_stubs;
+ bool mult_rcu;
+};
+
+static struct cgroup_struct_ops cgroup_struct_ops[MAX_CGROUP_BPF_ATTACH_TYPE];
+
+void cgroup_bpf_struct_ops_register(int atype, u32 type_id, void *cfi_stubs, bool mult_rcu)
+{
+ cgroup_struct_ops[atype].type_id = type_id;
+ cgroup_struct_ops[atype].cfi_stubs = cfi_stubs;
+ cgroup_struct_ops[atype].mult_rcu = mult_rcu;
+}
+
+static enum cgroup_bpf_attach_type find_atype_by_struct_ops_id(u32 type_id)
+{
+ enum cgroup_bpf_attach_type atype;
+
+ for (atype = 0; atype < MAX_CGROUP_BPF_ATTACH_TYPE; atype++) {
+ if (cgroup_bpf_is_struct_ops_atype(atype) &&
+ cgroup_struct_ops[atype].type_id &&
+ cgroup_struct_ops[atype].type_id == type_id)
+ return atype;
+ }
+ return CGROUP_BPF_ATTACH_TYPE_INVALID;
+}
+
/*
* cgroup bpf destruction makes heavy use of work items and there can be a lot
* of concurrent destructions. Use a separate workqueue so that cgroup bpf
@@ -306,6 +334,19 @@ static void bpf_cgroup_storages_link(struct bpf_cgroup_storage *storages[],
bpf_cgroup_storage_link(storages[stype], cgrp, attach_type);
}
+static void cgroup_struct_ops_link_detach_wake(struct bpf_cgroup_link *link, bool wake_poll)
+{
+ cgroup_put(link->cgroup);
+ link->cgroup = NULL;
+
+ bpf_map_put(link->map);
+ /* READ_ONCE in cgroup_struct_ops_link_poll */
+ WRITE_ONCE(link->map, NULL);
+
+ if (wake_poll)
+ wake_up_interruptible_poll(&link->wait_hup, EPOLLHUP);
+}
+
/* Called when bpf_cgroup_link is auto-detached from dying cgroup.
* It drops cgroup and bpf_prog refcounts, and marks bpf_link as defunct. It
* doesn't free link memory, which will eventually be done by bpf_link's
@@ -313,10 +354,96 @@ static void bpf_cgroup_storages_link(struct bpf_cgroup_storage *storages[],
*/
static void bpf_cgroup_link_auto_detach(struct bpf_cgroup_link *link)
{
+ if (link->map) {
+ cgroup_struct_ops_link_detach_wake(link, true);
+ return;
+ }
+
+ if (link->link.prog->expected_attach_type == BPF_LSM_CGROUP)
+ bpf_trampoline_unlink_cgroup_shim(link->link.prog);
cgroup_put(link->cgroup);
link->cgroup = NULL;
}
+static void bpf_cgroup_array_free_rcu(struct rcu_head *rcu)
+{
+ kfree(container_of(rcu, struct bpf_prog_array, rcu));
+}
+
+static void bpf_cgroup_array_free(struct bpf_prog_array *array,
+ enum cgroup_bpf_attach_type atype)
+{
+ if (!array || array == &bpf_empty_prog_array)
+ return;
+ if (cgroup_struct_ops[atype].mult_rcu)
+ /* RCU tasks trace grace period implies RCU grace period. */
+ call_rcu_tasks_trace(&array->rcu, bpf_cgroup_array_free_rcu);
+ else
+ kfree_rcu(array, rcu);
+}
+
+static void *bpf_cgroup_array_dummy(enum cgroup_bpf_attach_type atype)
+{
+ if (cgroup_bpf_is_struct_ops_atype(atype))
+ return cgroup_struct_ops[atype].cfi_stubs;
+ return bpf_prog_dummy();
+}
+
+static int bpf_cgroup_array_length(struct bpf_prog_array *array,
+ enum cgroup_bpf_attach_type atype)
+{
+ struct bpf_prog_array_item *item;
+ int cnt = 0;
+
+ for (item = array->items; item->prog; item++) {
+ if (item->prog != bpf_cgroup_array_dummy(atype))
+ cnt++;
+ }
+
+ return cnt;
+}
+
+static int bpf_cgroup_array_copy_to_user(struct bpf_prog_array *array,
+ __u32 __user *prog_ids, int cnt,
+ enum cgroup_bpf_attach_type atype)
+{
+ struct bpf_prog_array_item *item;
+ int i = 0;
+ u32 id;
+
+ for (item = array->items; item->prog && i < cnt; item++) {
+ if (item->prog == bpf_cgroup_array_dummy(atype))
+ continue;
+
+ if (cgroup_bpf_is_struct_ops_atype(atype))
+ id = bpf_struct_ops_id(item->kdata);
+ else
+ id = item->prog->aux->id;
+
+ if (copy_to_user(prog_ids + i, &id, sizeof(id)))
+ return -EFAULT;
+ i++;
+ }
+ return item->prog ? -ENOSPC : 0;
+}
+
+static int bpf_cgroup_array_delete_safe_at(struct bpf_prog_array *array,
+ int index, enum cgroup_bpf_attach_type atype)
+{
+ struct bpf_prog_array_item *item;
+
+ for (item = array->items; item->prog; item++) {
+ if (item->prog == bpf_cgroup_array_dummy(atype))
+ continue;
+ if (!index) {
+ WRITE_ONCE(item->prog, bpf_cgroup_array_dummy(atype));
+ return 0;
+ }
+ index--;
+ }
+ return -ENOENT;
+}
+
/**
* cgroup_bpf_release() - put references of all bpf programs and
* release all cgroup bpf data
@@ -346,18 +473,15 @@ static void cgroup_bpf_release(struct work_struct *work)
bpf_trampoline_unlink_cgroup_shim(pl->prog);
bpf_prog_put(pl->prog);
}
- if (pl->link) {
- if (pl->link->link.prog->expected_attach_type == BPF_LSM_CGROUP)
- bpf_trampoline_unlink_cgroup_shim(pl->link->link.prog);
+ if (pl->link)
bpf_cgroup_link_auto_detach(pl->link);
- }
kfree(pl);
static_branch_dec(&cgroup_bpf_enabled_key[atype]);
}
old_array = rcu_dereference_protected(
cgrp->bpf.effective[atype],
lockdep_is_held(&cgroup_mutex));
- bpf_prog_array_free(old_array);
+ bpf_cgroup_array_free(old_array, atype);
}
list_for_each_entry_safe(storage, stmp, storages, list_cg) {
@@ -399,6 +523,37 @@ static struct bpf_prog *prog_list_prog(struct bpf_prog_list *pl)
return NULL;
}
+static bool prog_list_is_struct_ops(const struct bpf_prog_list *pl)
+{
+ return pl->link && pl->link->map;
+}
+
+static void prog_list_init_item(struct bpf_prog_list *pl, struct bpf_prog_array_item *item)
+{
+ if (prog_list_is_struct_ops(pl)) {
+ item->kdata = bpf_struct_ops_map_kdata(pl->link->map);
+ return;
+ }
+
+ item->prog = prog_list_prog(pl);
+ bpf_cgroup_storages_assign(item->cgroup_storage, pl->storage);
+}
+
+static void prog_list_replace_item(struct bpf_prog_list *pl, struct bpf_prog_array_item *item)
+{
+ if (prog_list_is_struct_ops(pl))
+ WRITE_ONCE(item->kdata, bpf_struct_ops_map_kdata(pl->link->map));
+ else
+ WRITE_ONCE(item->prog, pl->link->link.prog);
+}
+
+static u32 prog_list_id(struct bpf_prog_list *pl)
+{
+ if (prog_list_is_struct_ops(pl))
+ return pl->link->map->id;
+ return prog_list_prog(pl)->aux->id;
+}
+
/* count number of elements in the list.
* it's slow but the list cannot be long
*/
@@ -408,7 +563,7 @@ static u32 prog_list_length(struct hlist_head *head, int *preorder_cnt)
u32 cnt = 0;
hlist_for_each_entry(pl, head, node) {
- if (!prog_list_prog(pl))
+ if (!pl->prog && !pl->link)
continue;
if (preorder_cnt && (pl->flags & BPF_F_PREORDER))
(*preorder_cnt)++;
@@ -482,7 +637,7 @@ static int compute_effective_progs(struct cgroup *cgrp,
init_bstart = bstart;
hlist_for_each_entry(pl, &p->bpf.progs[atype], node) {
- if (!prog_list_prog(pl))
+ if (!pl->prog && !pl->link)
continue;
if (pl->flags & BPF_F_PREORDER) {
@@ -492,9 +647,7 @@ static int compute_effective_progs(struct cgroup *cgrp,
item = &progs->items[fstart];
fstart++;
}
- item->prog = prog_list_prog(pl);
- bpf_cgroup_storages_assign(item->cgroup_storage,
- pl->storage);
+ prog_list_init_item(pl, item);
cnt++;
}
@@ -517,7 +670,7 @@ static void activate_effective_progs(struct cgroup *cgrp,
/* free prog array after grace period, since __cgroup_bpf_run_*()
* might be still walking the array
*/
- bpf_prog_array_free(old_array);
+ bpf_cgroup_array_free(old_array, atype);
}
/**
@@ -557,7 +710,7 @@ static int cgroup_bpf_inherit(struct cgroup *cgrp)
return 0;
cleanup:
for (i = 0; i < NR; i++)
- bpf_prog_array_free(arrays[i]);
+ bpf_cgroup_array_free(arrays[i], i);
for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p))
cgroup_bpf_put(p);
@@ -612,7 +765,7 @@ static int update_effective_progs(struct cgroup *cgrp,
if (percpu_ref_is_zero(&desc->bpf.refcnt)) {
if (unlikely(desc->bpf.inactive)) {
- bpf_prog_array_free(desc->bpf.inactive);
+ bpf_cgroup_array_free(desc->bpf.inactive, atype);
desc->bpf.inactive = NULL;
}
continue;
@@ -631,7 +784,7 @@ cleanup:
css_for_each_descendant_pre(css, &cgrp->self) {
struct cgroup *desc = container_of(css, struct cgroup, self);
- bpf_prog_array_free(desc->bpf.inactive);
+ bpf_cgroup_array_free(desc->bpf.inactive, atype);
desc->bpf.inactive = NULL;
}
@@ -870,7 +1023,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
old_pl_flags = pl->flags;
bpf_cgroup_storages_assign(old_storage, pl->storage);
} else {
- pl = kmalloc_obj(*pl);
+ pl = kzalloc_obj(*pl);
if (!pl) {
bpf_cgroup_storages_free(new_storage);
return -ENOMEM;
@@ -973,9 +1126,10 @@ static int effective_prog_pos(struct cgroup *cgrp,
init_bstart = bstart;
hlist_for_each_entry(pl, &p->bpf.progs[atype], node) {
- if (!prog_list_prog(pl))
- continue;
-
+ /*
+ * No detaching pl (NULL prog and link) is visible to the callers,
+ * so skip the check compute_effective_progs() needs.
+ */
if (pl->flags & BPF_F_PREORDER) {
if (pl == target_pl)
pos = bstart;
@@ -1022,7 +1176,7 @@ static void replace_effective_prog(struct cgroup *cgrp,
desc->bpf.effective[atype],
lockdep_is_held(&cgroup_mutex));
item = &progs->items[pos];
- WRITE_ONCE(item->prog, pl->link->link.prog);
+ prog_list_replace_item(pl, item);
}
}
@@ -1177,7 +1331,7 @@ static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog_list *pl,
lockdep_is_held(&cgroup_mutex));
/* Remove the program from the array */
- WARN_ONCE(bpf_prog_array_delete_safe_at(progs, pos),
+ WARN_ONCE(bpf_cgroup_array_delete_safe_at(progs, pos, atype),
"Failed to purge a prog from array at index %d", pos);
}
}
@@ -1287,7 +1441,15 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
if (effective_query && prog_attach_flags)
return -EINVAL;
- if (type == BPF_LSM_CGROUP) {
+ if (type == BPF_STRUCT_OPS) {
+ u32 type_id = attr->query.type_id;
+
+ atype = find_atype_by_struct_ops_id(type_id);
+ if (atype == CGROUP_BPF_ATTACH_TYPE_INVALID)
+ return -ENOENT;
+ from_atype = to_atype = atype;
+ flags = 0;
+ } else if (type == BPF_LSM_CGROUP) {
if (!effective_query && attr->query.prog_cnt &&
prog_ids && !prog_attach_flags)
return -EINVAL;
@@ -1307,7 +1469,7 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
if (effective_query) {
effective = rcu_dereference_protected(cgrp->bpf.effective[atype],
lockdep_is_held(&cgroup_mutex));
- total_cnt += bpf_prog_array_length(effective);
+ total_cnt += bpf_cgroup_array_length(effective, atype);
} else {
total_cnt += prog_list_length(&cgrp->bpf.progs[atype], NULL);
}
@@ -1337,35 +1499,33 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
if (effective_query) {
effective = rcu_dereference_protected(cgrp->bpf.effective[atype],
lockdep_is_held(&cgroup_mutex));
- cnt = min_t(int, bpf_prog_array_length(effective), total_cnt);
- ret = bpf_prog_array_copy_to_user(effective, prog_ids, cnt);
+ cnt = min_t(int, bpf_cgroup_array_length(effective, atype), total_cnt);
+ ret = bpf_cgroup_array_copy_to_user(effective, prog_ids, cnt, atype);
} else {
struct hlist_head *progs;
struct bpf_prog_list *pl;
- struct bpf_prog *prog;
u32 id;
progs = &cgrp->bpf.progs[atype];
cnt = min_t(int, prog_list_length(progs, NULL), total_cnt);
i = 0;
hlist_for_each_entry(pl, progs, node) {
- prog = prog_list_prog(pl);
- id = prog->aux->id;
+ id = prog_list_id(pl);
if (copy_to_user(prog_ids + i, &id, sizeof(id)))
return -EFAULT;
+ if (prog_attach_flags) {
+ flags = cgrp->bpf.flags[atype] |
+ (pl->flags & BPF_F_PREORDER);
+ if (copy_to_user(prog_attach_flags + i,
+ &flags, sizeof(flags)))
+ return -EFAULT;
+ }
if (++i == cnt)
break;
}
- if (prog_attach_flags) {
- flags = cgrp->bpf.flags[atype];
-
- for (i = 0; i < cnt; i++)
- if (copy_to_user(prog_attach_flags + i,
- &flags, sizeof(flags)))
- return -EFAULT;
+ if (prog_attach_flags)
prog_attach_flags += cnt;
- }
}
prog_ids += cnt;
@@ -2329,7 +2489,7 @@ static const struct bpf_func_proto bpf_sysctl_get_name_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
+ .arg2_type = ARG_PTR_TO_MEM | MEM_UNINIT | MEM_WRITE,
.arg3_type = ARG_MEM_SIZE,
.arg4_type = ARG_ANYTHING,
};
@@ -2772,6 +2932,274 @@ const struct bpf_verifier_ops cg_sockopt_verifier_ops = {
const struct bpf_prog_ops cg_sockopt_prog_ops = {
};
+static int __cgroup_struct_ops_link_detach(struct bpf_link *link, bool wake_poll)
+{
+ struct bpf_cgroup_link *cg_link = container_of(link, struct bpf_cgroup_link, link);
+ enum cgroup_bpf_attach_type atype;
+ struct bpf_prog_list *pl;
+ struct bpf_map *map;
+ struct cgroup *cgrp;
+
+ cgroup_lock();
+
+ cgrp = cg_link->cgroup;
+ if (!cgrp) {
+ cgroup_unlock();
+ return 0;
+ }
+
+ map = cg_link->map;
+ atype = bpf_struct_ops_map_cgroup_atype(map);
+
+ hlist_for_each_entry(pl, &cgrp->bpf.progs[atype], node) {
+ if (pl->link == cg_link)
+ break;
+ }
+ if (WARN_ON_ONCE(!pl)) {
+ cgroup_unlock();
+ return -ENOENT;
+ }
+
+ /* mark deleted so compute_effective_progs() skips it */
+ pl->link = NULL;
+ if (update_effective_progs(cgrp, atype)) {
+ pl->link = cg_link;
+ purge_effective_progs(cgrp, pl, atype);
+ }
+
+ hlist_del(&pl->node);
+ cgroup_struct_ops_link_detach_wake(cg_link, wake_poll);
+ cgrp->bpf.revisions[atype]++;
+
+ kfree(pl);
+ static_branch_dec(&cgroup_bpf_enabled_key[atype]);
+
+ cgroup_unlock();
+
+ return 0;
+}
+
+static int cgroup_struct_ops_link_detach(struct bpf_link *link)
+{
+ return __cgroup_struct_ops_link_detach(link, true);
+}
+
+static void cgroup_struct_ops_link_dealloc(struct bpf_link *link)
+{
+ struct bpf_cgroup_link *cg_link = container_of(link, struct bpf_cgroup_link, link);
+
+ __cgroup_struct_ops_link_detach(link, false);
+ kfree(cg_link);
+}
+
+static void cgroup_struct_ops_link_show_fdinfo(const struct bpf_link *link, struct seq_file *seq)
+{
+ struct bpf_cgroup_link *cg_link =
+ container_of(link, struct bpf_cgroup_link, link);
+
+ cgroup_lock();
+ if (!cg_link->cgroup) {
+ cgroup_unlock();
+ return;
+ }
+
+ seq_printf(seq, "map_id:\t%u\n", cg_link->map->id);
+ seq_printf(seq, "cgroup_id:\t%llu\n", cgroup_id(cg_link->cgroup));
+ cgroup_unlock();
+}
+
+static int cgroup_struct_ops_link_fill_link_info(const struct bpf_link *link,
+ struct bpf_link_info *info)
+{
+ struct bpf_cgroup_link *cg_link = container_of(link, struct bpf_cgroup_link, link);
+
+ cgroup_lock();
+ if (!cg_link->cgroup) {
+ cgroup_unlock();
+ return 0;
+ }
+
+ info->struct_ops.map_id = cg_link->map->id;
+ info->struct_ops.cgroup_id = cgroup_id(cg_link->cgroup);
+ cgroup_unlock();
+ return 0;
+}
+
+static int cgroup_struct_ops_link_update(struct bpf_link *link, struct bpf_map *new_map,
+ struct bpf_map *expected_old_map)
+{
+ struct bpf_cgroup_link *cg_link = container_of(link, struct bpf_cgroup_link, link);
+ enum cgroup_bpf_attach_type atype;
+ struct bpf_prog_list *pl;
+ struct bpf_map *old_map;
+ struct cgroup *cgrp;
+ bool found = false;
+ int err;
+
+ if (!bpf_struct_ops_valid_to_reg(new_map))
+ return -EINVAL;
+
+ cgroup_lock();
+
+ cgrp = cg_link->cgroup;
+ if (!cgrp) {
+ err = -ENOLINK;
+ goto out;
+ }
+
+ old_map = cg_link->map;
+ err = bpf_struct_ops_link_update_check(new_map, old_map, expected_old_map);
+ if (err)
+ goto out;
+
+ atype = bpf_struct_ops_map_cgroup_atype(new_map);
+
+ hlist_for_each_entry(pl, &cgrp->bpf.progs[atype], node) {
+ if (pl->link == cg_link) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ bpf_map_inc(new_map);
+ WRITE_ONCE(cg_link->map, new_map);
+ replace_effective_prog(cgrp, atype, pl);
+ bpf_map_put(old_map);
+ cgrp->bpf.revisions[atype]++;
+
+out:
+ cgroup_unlock();
+ return err;
+}
+
+static __poll_t cgroup_struct_ops_link_poll(struct file *file, struct poll_table_struct *pts)
+{
+ struct bpf_cgroup_link *link = file->private_data;
+
+ poll_wait(file, &link->wait_hup, pts);
+
+ return READ_ONCE(link->map) ? 0 : EPOLLHUP;
+}
+
+static const struct bpf_link_ops cgroup_struct_ops_link_ops = {
+ .dealloc = cgroup_struct_ops_link_dealloc,
+ .detach = cgroup_struct_ops_link_detach,
+ .show_fdinfo = cgroup_struct_ops_link_show_fdinfo,
+ .fill_link_info = cgroup_struct_ops_link_fill_link_info,
+ .update_map = cgroup_struct_ops_link_update,
+ .poll = cgroup_struct_ops_link_poll,
+};
+
+int cgroup_bpf_struct_ops_attach(struct bpf_map *map, const union bpf_attr *attr)
+{
+ u32 flags = attr->link_create.flags;
+ u32 pl_flags = (flags & BPF_F_PREORDER) | BPF_F_ALLOW_MULTI;
+ enum cgroup_bpf_attach_type atype;
+ struct bpf_link_primer link_primer;
+ struct bpf_cgroup_link *link;
+ struct bpf_prog_list *pl = NULL;
+ struct hlist_head *progs;
+ struct cgroup *cgrp;
+ int err;
+
+ if (flags & ~BPF_F_LINK_ATTACH_MASK)
+ return -EINVAL;
+
+ /*
+ * Attaching struct_ops to cgroup is through link only. All relative
+ * position must be corresponding to a link id or fd.
+ */
+ if (attr->link_create.cgroup.relative_fd && !(flags & BPF_F_LINK))
+ return -EINVAL;
+
+ link = kzalloc_obj(*link, GFP_USER);
+ if (!link)
+ return -ENOMEM;
+
+ bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS,
+ &cgroup_struct_ops_link_ops, NULL,
+ attr->link_create.attach_type);
+
+ err = bpf_link_prime(&link->link, &link_primer);
+ if (err) {
+ kfree(link);
+ return err;
+ }
+
+ cgrp = cgroup_get_from_fd(attr->link_create.target_fd);
+ if (IS_ERR(cgrp)) {
+ err = PTR_ERR(cgrp);
+ goto cleanup;
+ }
+
+ bpf_map_inc(map);
+ link->map = map;
+ link->cgroup = cgrp;
+ init_waitqueue_head(&link->wait_hup);
+
+ atype = bpf_struct_ops_map_cgroup_atype(map);
+ progs = &cgrp->bpf.progs[atype];
+
+ cgroup_lock();
+
+ if (attr->link_create.cgroup.expected_revision &&
+ attr->link_create.cgroup.expected_revision != cgrp->bpf.revisions[atype]) {
+ err = -ESTALE;
+ goto unlock;
+ }
+
+ if (prog_list_length(progs, NULL) >= BPF_CGROUP_MAX_PROGS) {
+ err = -E2BIG;
+ goto unlock;
+ }
+
+ pl = kzalloc_obj(*pl);
+ if (!pl) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ pl->link = link;
+ pl->flags = pl_flags;
+ cgrp->bpf.flags[atype] = BPF_F_ALLOW_MULTI;
+
+ err = insert_pl_to_hlist(pl, progs, NULL, link,
+ flags | BPF_F_ALLOW_MULTI, attr->link_create.cgroup.relative_fd);
+ if (err)
+ goto unlock;
+
+ err = update_effective_progs(cgrp, atype);
+ if (err) {
+ hlist_del(&pl->node);
+ goto unlock;
+ }
+
+ cgrp->bpf.revisions[atype]++;
+ static_branch_inc(&cgroup_bpf_enabled_key[atype]);
+
+ cgroup_unlock();
+
+ return bpf_link_settle(&link_primer);
+
+unlock:
+ cgroup_unlock();
+
+cleanup:
+ kfree(pl);
+ if (link->cgroup) {
+ cgroup_put(link->cgroup);
+ link->cgroup = NULL;
+ bpf_map_put(link->map);
+ link->map = NULL;
+ }
+ bpf_link_cleanup(&link_primer);
+ return err;
+}
+
/* Common helpers for cgroup hooks. */
const struct bpf_func_proto *
cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
diff --git a/kernel/bpf/cnum.c b/kernel/bpf/cnum.c
index 86142cb2aee5..8ec5187d7c75 100644
--- a/kernel/bpf/cnum.c
+++ b/kernel/bpf/cnum.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#include <linux/bits.h>
+#include <linux/bpf_verifier.h> /* for imod() */
#define T 32
#include "cnum_defs.h"
@@ -118,3 +119,38 @@ struct cnum64 cnum64_cnum32_intersect(struct cnum64 a, struct cnum32 b)
}
return t;
}
+
+/* Intersect 'in' with the set of integers defined by equation 'base + step * k'. */
+struct cnum64 cnum64_intersect_linear(struct cnum64 in, u16 base, u16 step)
+{
+ s64 smin = cnum64_smin(in);
+ s64 smax = cnum64_smax(in);
+ s64 lo, hi;
+ u16 d;
+
+ if (step <= 1 || cnum64_is_empty(in) || cnum64_srange_overflow(in))
+ return in;
+ /*
+ * Round smin up to the next value congruent to 'base' modulo 'step',
+ * i.e. increase smin by d = (base - smin) mod step:
+ *
+ * |<---- d ---->|
+ * |-----------|=============|...
+ * base+step*k smin base+step*(k+1)
+ */
+ d = imod(base - imod(smin, step), step);
+ if ((u64)smax - (u64)smin < d)
+ return CNUM64_EMPTY;
+ lo = smin + d;
+ /*
+ * Round smax down to the previous value congruent to 'base' modulo 'step',
+ * i.e. decrease smax by d = (smax - base) mod step:
+ *
+ * |<--- d --->|
+ * ...|===========|-------------|
+ * base+step*k smax base+step*(k+1)
+ */
+ d = imod(imod(smax, step) - base, step);
+ hi = smax - d;
+ return cnum64_from_srange(lo, hi);
+}
diff --git a/kernel/bpf/cnum_defs.h b/kernel/bpf/cnum_defs.h
index a90e317e3578..05b17a8b4814 100644
--- a/kernel/bpf/cnum_defs.h
+++ b/kernel/bpf/cnum_defs.h
@@ -33,7 +33,12 @@ struct cnum_t FN(from_srange)(st min, st max)
return (struct cnum_t){ .base = base, .size = size };
}
-/* True if this cnum represents two unsigned ranges. */
+/*
+ * True if this cnum represents two unsigned ranges.
+ *
+ * The caller should ensure !is_empty(cnum) holds when calling
+ * this function.
+ */
static inline bool FN(urange_overflow)(struct cnum_t cnum)
{
/* Same as cnum.base + cnum.size > UT_MAX but avoids overflow */
@@ -44,6 +49,9 @@ static inline bool FN(urange_overflow)(struct cnum_t cnum)
* cnum{T}_umin / cnum{T}_umax query an unsigned range represented by this cnum.
* If cnum represents a range crossing the UT_MAX/0 boundary, the unbound range
* [0..UT_MAX] is returned.
+ *
+ * The caller should ensure !is_empty(cnum) holds when calling
+ * cnum{T}_umin / cnum{T}_umax.
*/
ut FN(umin)(struct cnum_t cnum)
{
@@ -57,7 +65,12 @@ ut FN(umax)(struct cnum_t cnum)
}
EXPORT_SYMBOL_GPL(FN(umax));
-/* True if this cnum represents two signed ranges. */
+/*
+ * True if this cnum represents two signed ranges.
+ *
+ * The caller should ensure !is_empty(cnum) holds when calling
+ * this function.
+ */
static inline bool FN(srange_overflow)(struct cnum_t cnum)
{
return FN(contains)(cnum, (ut)ST_MAX) && FN(contains)(cnum, (ut)ST_MIN);
@@ -67,19 +80,18 @@ static inline bool FN(srange_overflow)(struct cnum_t cnum)
* cnum{T}_smin / cnum{T}_smax query a signed range represented by this cnum.
* If cnum represents a range crossing the ST_MAX/ST_MIN boundary, the unbound range
* [ST_MIN..ST_MAX] is returned.
+ *
+ * The caller should ensure !is_empty(cnum) holds when calling
+ * cnum{T}_smin / cnum{T}_smax.
*/
st FN(smin)(struct cnum_t cnum)
{
- return FN(srange_overflow)(cnum)
- ? ST_MIN
- : min((st)cnum.base, (st)(cnum.base + cnum.size));
+ return FN(srange_overflow)(cnum) ? ST_MIN : (st)cnum.base;
}
st FN(smax)(struct cnum_t cnum)
{
- return FN(srange_overflow)(cnum)
- ? ST_MAX
- : max((st)cnum.base, (st)(cnum.base + cnum.size));
+ return FN(srange_overflow)(cnum) ? ST_MAX : (st)(cnum.base + cnum.size);
}
/*
@@ -181,11 +193,55 @@ void FN(intersect_with_srange)(struct cnum_t *dst, st min, st max)
static inline struct cnum_t FN(normalize)(struct cnum_t cnum)
{
- if (cnum.size == UT_MAX && cnum.base != 0 && cnum.base != (ut)ST_MAX)
+ if (cnum.size == UT_MAX)
cnum.base = 0;
return cnum;
}
+/*
+ * Return a smallest arc containing both 'a' and 'b'.
+ * Break equal-size ties by choosing the smaller base.
+ */
+struct cnum_t FN(union)(struct cnum_t a, struct cnum_t b)
+{
+ struct cnum_t b1, ab, ba;
+ ut end;
+
+ if (FN(is_empty)(a))
+ return b;
+ if (FN(is_empty)(b))
+ return a;
+
+ /*
+ * Rotate so that a1.base == 0 and a1.end == a.size.
+ * Normalize b1 to preserve the full-circle representation.
+ */
+ b1 = FN(normalize)((struct cnum_t){ b.base - a.base, b.size });
+ end = max(a.size, (ut)(b1.base + b1.size));
+
+ if (FN(urange_overflow)(b1)) {
+ /* a1 reaches b1's main arc: together they cover the circle. */
+ if (b1.base <= a.size)
+ return (struct cnum_t){ 0, UT_MAX };
+
+ /* Extend b1's tail through a1's end, then rotate back. */
+ return FN(normalize)((struct cnum_t){ b.base, end - b1.base });
+ }
+
+ /* ab, rotated back, covers both nonwrapping arcs. */
+ ab = (struct cnum_t){ a.base, end };
+ if (b1.base <= a.size)
+ return FN(normalize)(ab);
+
+ /* The arcs are disjoint; ba is the other possible covering arc. */
+ ba = (struct cnum_t){ b.base, a.size - b1.base };
+ if (ba.size < ab.size ||
+ (ba.size == ab.size && ba.base < ab.base))
+ ab = ba;
+
+ return FN(normalize)(ab);
+}
+
struct cnum_t FN(add)(struct cnum_t a, struct cnum_t b)
{
if (FN(is_empty)(a) || FN(is_empty)(b))
@@ -210,12 +266,7 @@ bool FN(is_empty)(struct cnum_t cnum)
bool FN(contains)(struct cnum_t cnum, ut v)
{
- if (FN(is_empty)(cnum))
- return false;
- if (FN(urange_overflow)(cnum))
- return v >= cnum.base || v <= (ut)cnum.base + cnum.size;
- else
- return v >= cnum.base && v <= (ut)cnum.base + cnum.size;
+ return !FN(is_empty)(cnum) && v - cnum.base <= cnum.size;
}
bool FN(is_const)(struct cnum_t cnum)
diff --git a/kernel/bpf/const_fold.c b/kernel/bpf/const_fold.c
index 7f1b30059cc8..f9edfde965dc 100644
--- a/kernel/bpf/const_fold.c
+++ b/kernel/bpf/const_fold.c
@@ -180,9 +180,12 @@ static void const_reg_xfer(struct bpf_verifier_env *env, struct const_arg_info *
bool is_ldsx = mode == BPF_MEMSX;
int off = src->val + insn->off;
u64 val = 0;
+ u32 cnt;
if (!bpf_map_is_rdonly(map) || !map->ops->map_direct_value_addr ||
off < 0 || off + size > map->value_size ||
+ /* so are the addresses of functions that the map points to */
+ bpf_map_range_func_ptrs(env, map, off, size, &cnt) ||
bpf_map_direct_read(map, off, size, &val, is_ldsx)) {
*dst = unknown;
break;
@@ -191,7 +194,8 @@ static void const_reg_xfer(struct bpf_verifier_env *env, struct const_arg_info *
dst->val = val;
break;
case BPF_JMP:
- if (opcode != BPF_CALL)
+ /* both 'call imm' and 'callx reg' clobber caller saved registers */
+ if (BPF_OP(insn->code) != BPF_CALL)
break;
process_call:
for (r = BPF_REG_0; r <= BPF_REG_5; r++)
@@ -353,7 +357,7 @@ int bpf_prune_dead_branches(struct bpf_verifier_env *env)
if (!bpf_insn_is_cond_jump(insn->code))
continue;
- if (bpf_is_may_goto_insn(insn))
+ if (BPF_OP(insn->code) == BPF_JCOND)
continue;
if (!(aux->const_reg_mask & BIT(insn->dst_reg)))
@@ -394,5 +398,7 @@ int bpf_prune_dead_branches(struct bpf_verifier_env *env)
/* recompute postorder, since CFG has changed */
kvfree(env->cfg.insn_postorder);
env->cfg.insn_postorder = NULL;
+ kvfree(env->cfg.postorder_nums);
+ env->cfg.postorder_nums = NULL;
return bpf_compute_postorder(env);
}
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 7f11555a5070..3030beb531d9 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -18,6 +18,7 @@
*/
#include <uapi/linux/btf.h>
+#include <linux/ctype.h>
#include <linux/filter.h>
#include <linux/sched/signal.h>
#include <linux/skbuff.h>
@@ -142,10 +143,6 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
mutex_init(&fp->aux->dst_mutex);
mutex_init(&fp->aux->st_ops_assoc_mutex);
-#ifdef CONFIG_BPF_SYSCALL
- bpf_prog_stream_init(fp);
-#endif
-
return fp;
}
@@ -175,7 +172,6 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
}
return prog;
}
-EXPORT_SYMBOL_GPL(bpf_prog_alloc);
int bpf_prog_alloc_jited_linfo(struct bpf_prog *prog)
{
@@ -273,6 +269,7 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
memcpy(fp, fp_old, fp_old->pages * PAGE_SIZE);
fp->pages = pages;
fp->aux->prog = fp;
+ bpf_prog_dev_bound_moved(fp);
/* We keep fp->aux from fp_old around in the new
* reallocated structure.
@@ -289,6 +286,9 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
void __bpf_prog_free(struct bpf_prog *fp)
{
if (fp->aux) {
+#ifdef CONFIG_BPF_SYSCALL
+ bpf_prog_stream_free(fp);
+#endif
mutex_destroy(&fp->aux->used_maps_mutex);
mutex_destroy(&fp->aux->dst_mutex);
mutex_destroy(&fp->aux->st_ops_assoc_mutex);
@@ -590,6 +590,10 @@ bpf_prog_ksym_set_name(struct bpf_prog *prog)
prog->aux->func_info[prog->aux->func_idx].type_id);
func_name = btf_name_by_offset(prog->aux->btf, type->name_off);
snprintf(sym, (size_t)(end - sym), "_%s", func_name);
+ /* the name of a function of Rust is not an identifier */
+ for (; *sym; sym++)
+ if (!isalnum(*sym) && *sym != '_' && *sym != '.')
+ *sym = '_';
return;
}
@@ -1555,8 +1559,7 @@ void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other)
* know whether fp here is the clone or the original.
*/
fp->aux->prog = fp;
- if (fp->aux->offload)
- fp->aux->offload->prog = fp;
+ bpf_prog_dev_bound_moved(fp);
bpf_prog_clone_free(fp_other);
}
@@ -1677,7 +1680,6 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
{
return 0;
}
-EXPORT_SYMBOL_GPL(__bpf_call_base);
/* All UAPI available opcodes. */
#define BPF_INSN_MAP(INSN_2, INSN_3) \
@@ -1840,6 +1842,7 @@ bool bpf_opcode_in_insntable(u8 code)
[BPF_LD | BPF_IND | BPF_H] = true,
[BPF_LD | BPF_IND | BPF_W] = true,
[BPF_JMP | BPF_JA | BPF_X] = true,
+ [BPF_JMP | BPF_CALL | BPF_X] = true,
[BPF_JMP | BPF_JCOND] = true,
};
#undef BPF_INSN_3_TBL
@@ -2634,11 +2637,68 @@ static bool bpf_prog_select_interpreter(struct bpf_prog *fp)
return select_interpreter;
}
+/*
+ * Whether a function has an insn that works with the arena base, which a JIT
+ * sets up in the prologue when bpf_prog_uses_arena_insns() says so. Decided on
+ * the final insns of the function: an access through an arena pointer, or
+ * through a number in a program loaded with BPF_F_ARENA_SCALAR, has been
+ * rewritten to a BPF_PROBE_MEM32 form by then, a cast to the user address
+ * space keeps BPF_ADDR_SPACE_CAST, and a kfunc call with an __arena argument,
+ * which the JIT rebases, has a function model that says so. A load of an
+ * __arena struct member is rewritten with an immediate and needs none;
+ * dereferencing the result does.
+ */
+bool bpf_prog_has_arena_insns(const struct bpf_prog *prog)
+{
+ const struct bpf_insn *insn = prog->insnsi;
+ const struct btf_func_model *m;
+ int i, j;
+
+ if (!prog->aux->arena)
+ return false;
+
+ for (i = 0; i < prog->len; i++, insn++) {
+ switch (BPF_CLASS(insn->code)) {
+ case BPF_LDX:
+ if (BPF_MODE(insn->code) == BPF_PROBE_MEM32 ||
+ BPF_MODE(insn->code) == BPF_PROBE_MEM32SX)
+ return true;
+ break;
+ case BPF_ST:
+ if (BPF_MODE(insn->code) == BPF_PROBE_MEM32)
+ return true;
+ break;
+ case BPF_STX:
+ if (BPF_MODE(insn->code) == BPF_PROBE_MEM32 ||
+ BPF_MODE(insn->code) == BPF_PROBE_ATOMIC)
+ return true;
+ break;
+ case BPF_ALU64:
+ if (BPF_OP(insn->code) == BPF_MOV && BPF_SRC(insn->code) == BPF_X &&
+ insn->off == BPF_ADDR_SPACE_CAST)
+ return true;
+ break;
+ case BPF_JMP:
+ if (!bpf_pseudo_kfunc_call(insn))
+ break;
+ m = bpf_jit_find_kfunc_model(prog, insn);
+ if (!m)
+ break;
+ for (j = 0; j < m->nr_args; j++)
+ if (m->arg_flags[j] & BTF_FMODEL_ARENA_ARG)
+ return true;
+ break;
+ }
+ }
+ return false;
+}
+
static struct bpf_prog *bpf_prog_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
#ifdef CONFIG_BPF_JIT
struct bpf_prog *orig_prog;
+ prog->aux->uses_arena_insns = bpf_prog_has_arena_insns(prog);
if (!bpf_prog_need_blind(prog))
return bpf_int_jit_compile(env, prog);
@@ -2742,7 +2802,6 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
{
return __bpf_prog_select_runtime(NULL, fp, err);
}
-EXPORT_SYMBOL_GPL(bpf_prog_select_runtime);
static unsigned int __bpf_prog_ret1(const void *ctx,
const struct bpf_insn *insn)
@@ -2803,6 +2862,11 @@ void bpf_prog_array_free_sleepable(struct bpf_prog_array *progs)
call_rcu_tasks_trace(&progs->rcu, __bpf_prog_array_free_sleepable_cb);
}
+struct bpf_prog *bpf_prog_dummy(void)
+{
+ return &dummy_bpf_prog.prog;
+}
+
int bpf_prog_array_length(struct bpf_prog_array *array)
{
struct bpf_prog_array_item *item;
@@ -3035,13 +3099,18 @@ void __bpf_free_used_maps(struct bpf_prog_aux *aux,
bool sleepable;
u32 i;
- sleepable = aux->prog->sleepable;
+ sleepable = bpf_prog_runs_sleepable(aux->prog);
for (i = 0; i < len; i++) {
map = used_maps[i];
if (map->ops->map_poke_untrack)
map->ops->map_poke_untrack(map, aux);
if (sleepable)
atomic64_dec(&map->sleepable_refcnt);
+ /*
+ * The program that didn't load is not a user of the map. libbpf
+ * loads the program again to get the log of the verifier.
+ */
+ cmpxchg(&map->user, (unsigned long)aux, 0);
bpf_map_put(map);
}
}
@@ -3073,15 +3142,42 @@ static void bpf_free_used_btfs(struct bpf_prog_aux *aux)
kfree(aux->used_btfs);
}
-static void bpf_prog_free_deferred(struct work_struct *work)
+static void bpf_prog_free_images(struct work_struct *work)
{
struct bpf_prog_aux *aux;
int i;
+ aux = container_of(to_rcu_work(work), struct bpf_prog_aux, rwork);
+ for (i = 0; i < aux->real_func_cnt; i++) {
+ /* We can just unlink the subprog poke descriptor table as
+ * it was originally linked to the main program and is also
+ * released along with it.
+ */
+ aux->func[i]->aux->poke_tab = NULL;
+ bpf_jit_free(aux->func[i]);
+ }
+ if (aux->real_func_cnt) {
+ kfree(aux->func);
+ bpf_prog_unlock_free(aux->prog);
+ } else {
+ bpf_jit_free(aux->prog);
+ }
+}
+
+static void bpf_prog_free_deferred(struct work_struct *work)
+{
+ struct bpf_prog_aux *aux;
+ bool ksyms;
+
aux = container_of(work, struct bpf_prog_aux, work);
+ /*
+ * A prog that still runs when its last reference goes needs its symbols,
+ * by which its exception table is found. It has ended by now.
+ */
+ ksyms = aux->real_func_cnt || !list_empty(&aux->ksym.lnode);
+ bpf_prog_kallsyms_del_all(aux->prog);
#ifdef CONFIG_BPF_SYSCALL
bpf_free_kfunc_btf_tab(aux->kfunc_btf_tab);
- bpf_prog_stream_free(aux->prog);
#endif
#ifdef CONFIG_CGROUP_BPF
if (aux->cgroup_atype != CGROUP_BPF_ATTACH_TYPE_INVALID)
@@ -3098,20 +3194,12 @@ static void bpf_prog_free_deferred(struct work_struct *work)
#endif
if (aux->dst_trampoline)
bpf_trampoline_put(aux->dst_trampoline);
- for (i = 0; i < aux->real_func_cnt; i++) {
- /* We can just unlink the subprog poke descriptor table as
- * it was originally linked to the main program and is also
- * released along with it.
- */
- aux->func[i]->aux->poke_tab = NULL;
- bpf_jit_free(aux->func[i]);
- }
- if (aux->real_func_cnt) {
- kfree(aux->func);
- bpf_prog_unlock_free(aux->prog);
- } else {
- bpf_jit_free(aux->prog);
- }
+ /* The symbols are looked up under rcu_read_lock() */
+ INIT_RCU_WORK(&aux->rwork, bpf_prog_free_images);
+ if (ksyms)
+ queue_rcu_work(system_percpu_wq, &aux->rwork);
+ else
+ bpf_prog_free_images(&aux->rwork.work);
}
void bpf_prog_free(struct bpf_prog *fp)
@@ -3124,7 +3212,6 @@ void bpf_prog_free(struct bpf_prog *fp)
INIT_WORK(&aux->work, bpf_prog_free_deferred);
schedule_work(&aux->work);
}
-EXPORT_SYMBOL_GPL(bpf_prog_free);
/* RNG for unprivileged user space with separated state from prandom_u32(). */
static DEFINE_PER_CPU(struct rnd_state, bpf_user_rnd_state);
@@ -3301,6 +3388,117 @@ bool __weak bpf_jit_supports_kfunc_call(void)
return false;
}
+/* Return TRUE if the JIT backend supports callx (indirect call) instruction. */
+bool __weak bpf_jit_supports_callx(void)
+{
+ return false;
+}
+
+bool __weak bpf_jit_supports_kfunc_ret_reg_pair(void)
+{
+ return false;
+}
+
+/* unwind_goto and unwind_exit */
+bool __weak bpf_jit_supports_unwind(void)
+{
+ return false;
+}
+
+/*
+ * How this arch places a by-value kfunc argument, or NULL for one that has
+ * not opted in and so only takes an argument of a single eightbyte, which
+ * every convention places in slot order.
+ */
+const struct bpf_jit_arg_abi * __weak bpf_jit_arg_abi(void)
+{
+ return NULL;
+}
+
+u32 bpf_jit_place_args(const struct bpf_jit_arg_abi *abi,
+ const struct btf_func_model *fm, u8 *pos_of_slot)
+{
+ u32 i, k, nslots, slot = 0, nregs_used = 0, stack_off = 0;
+ bool on_stack = false;
+
+ for (i = 0; i < fm->nr_args; i++) {
+ bool align16 = fm->arg_flags[i] & BTF_FMODEL_ALIGN16_ARG;
+ u32 pos;
+
+ nslots = btf_func_model_arg_slots(fm, i);
+
+ if (align16 && abi->even_reg_align)
+ nregs_used = round_up(nregs_used, 2);
+
+ if (!on_stack && nregs_used + nslots <= abi->nr_arg_regs) {
+ /* wholly in registers */
+ pos = nregs_used;
+ nregs_used += nslots;
+ } else if (!on_stack && abi->split_at_boundary) {
+ /* the last registers hold what fits, the stack the rest */
+ pos = nregs_used;
+ stack_off = (nregs_used + nslots - abi->nr_arg_regs) * BPF_REG_SIZE;
+ nregs_used = abi->nr_arg_regs;
+ on_stack = true;
+ } else {
+ /* wholly on the stack */
+ if (align16 && abi->even_stack_align)
+ stack_off = round_up(stack_off, 2 * BPF_REG_SIZE);
+ pos = abi->nr_arg_regs + stack_off / BPF_REG_SIZE;
+ stack_off += nslots * BPF_REG_SIZE;
+ if (!abi->backfill_after_stack)
+ on_stack = true;
+ }
+
+ for (k = 0; k < nslots; k++)
+ pos_of_slot[slot + k] = pos + k;
+ slot += nslots;
+ }
+
+ return slot;
+}
+
+u32 bpf_jit_plan_arg_moves(const struct bpf_jit_arg_abi *abi,
+ const struct btf_func_model *fm,
+ struct bpf_jit_arg_move *moves)
+{
+ u8 pos_of_slot[MAX_BPF_FUNC_ARG_SLOTS];
+ u32 nslots, n = 0, s, back;
+
+ nslots = bpf_jit_place_args(abi, fm, pos_of_slot);
+ back = nslots;
+
+ /*
+ * An argument is two eightbytes at most, so it frees one register at
+ * most and only one argument ever moves down. Its destination is
+ * still in use, so carry it in the scratch. Only a lower slot can
+ * take the one it leaves, so the walk reaches it first.
+ */
+ for (s = nslots; s > 0; s--) {
+ u8 slot = s - 1, pos = pos_of_slot[slot];
+
+ if (pos == slot)
+ continue;
+
+ if (pos < slot) {
+ moves[n].dst = BPF_JIT_ARG_TMP;
+ back = slot;
+ } else {
+ moves[n].dst = pos;
+ }
+ moves[n].src = slot;
+ n++;
+ }
+
+ if (back < nslots) {
+ moves[n].dst = pos_of_slot[back];
+ moves[n].src = BPF_JIT_ARG_TMP;
+ n++;
+ }
+
+ return n;
+}
+
bool __weak bpf_jit_supports_stack_args(void)
{
return false;
@@ -3321,6 +3519,12 @@ bool __weak bpf_jit_supports_arena(void)
return false;
}
+/* Whether JIT takes BPF_REG_AX as the address of arena access */
+bool __weak bpf_jit_supports_arena_scalar(void)
+{
+ return false;
+}
+
bool __weak bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
{
return false;
@@ -3386,6 +3590,19 @@ bool __weak bpf_jit_supports_private_stack(void)
return false;
}
+/*
+ * Return TRUE if the JIT lays out frames of up to MAX_BPF_STACK_JIT bytes.
+ * Its prologue, epilogue and tail call sequences must encode such frame
+ * sizes and a private stack must be sized from the program's depth. The
+ * budget is only granted alongside bpf_jit_supports_subprog_tailcalls(),
+ * whose tail calls land before the target sets up its own frame; see
+ * bpf_prog_stack_limit().
+ */
+bool __weak bpf_jit_supports_large_stack(void)
+{
+ return false;
+}
+
void __weak arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie)
{
}
diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c
index 5e59ab896f05..eb87131b1bef 100644
--- a/kernel/bpf/cpumap.c
+++ b/kernel/bpf/cpumap.c
@@ -45,6 +45,7 @@
*/
#define CPU_MAP_BULK_SIZE 8 /* 8 == one cacheline on 64-bit archs */
+#define CPU_MAP_HELD_DEVS 2048
struct bpf_cpu_map_entry;
struct bpf_cpu_map;
@@ -74,6 +75,13 @@ struct bpf_cpu_map_entry {
struct completion kthread_running;
struct rcu_work free_work;
+
+ /*
+ * Every frame and skb in the queue holds a reference to its device.
+ * The kthread keeps it until nothing is left in GRO.
+ */
+ struct net_device *held[CPU_MAP_HELD_DEVS];
+ u32 nr_held;
};
struct bpf_cpu_map {
@@ -129,9 +137,11 @@ static void __cpu_map_ring_cleanup(struct ptr_ring *ring)
WARN_ON_ONCE(1);
if (unlikely(__ptr_test_bit(0, &ptr))) {
__ptr_clear_bit(0, &ptr);
+ dev_put(((struct sk_buff *)ptr)->dev);
kfree_skb(ptr);
continue;
}
+ dev_put(((struct xdp_frame *)ptr)->dev_rx);
xdp_return_frame(ptr);
}
}
@@ -311,7 +321,7 @@ static int cpu_map_kthread_run(void *data)
void *frames[CPUMAP_BATCH];
void *skbs[CPUMAP_BATCH];
u32 i, n, m;
- bool empty;
+ bool empty, full;
/* Release CPU reschedule checks */
if (__ptr_ring_empty(rcpu->queue)) {
@@ -344,10 +354,12 @@ static int cpu_map_kthread_run(void *data)
struct sk_buff *skb = f;
__ptr_clear_bit(0, &skb);
+ rcpu->held[rcpu->nr_held++] = skb->dev;
skbs[ret.skb_n++] = skb;
continue;
}
+ rcpu->held[rcpu->nr_held++] = ((struct xdp_frame *)f)->dev_rx;
frames[ret.xdp_n++] = f;
page = virt_to_page(f);
@@ -398,10 +410,13 @@ stats:
/* Flush either every 64 packets or in case of empty ring */
packets += n;
empty = __ptr_ring_empty(rcpu->queue);
- if (packets >= NAPI_POLL_WEIGHT || empty) {
- cpu_map_gro_flush(rcpu, empty);
+ full = empty || rcpu->nr_held > CPU_MAP_HELD_DEVS - CPUMAP_BATCH;
+ if (packets >= NAPI_POLL_WEIGHT || full) {
+ cpu_map_gro_flush(rcpu, full);
packets = 0;
}
+ while (full && rcpu->nr_held)
+ dev_put(rcpu->held[--rcpu->nr_held]);
local_bh_enable(); /* resched point, may call do_softirq() */
}
@@ -738,12 +753,14 @@ static void bq_flush_to_queue(struct xdp_bulk_queue *bq)
for (i = 0; i < bq->count; i++) {
struct xdp_frame *xdpf = bq->q[i];
- int err;
- err = __ptr_ring_produce(q, xdpf);
- if (err) {
+ if (__ptr_ring_full(q)) {
drops++;
xdp_return_frame_rx_napi(xdpf);
+ } else {
+ /* The device cannot go before the flush, from now on it can */
+ dev_hold(xdpf->dev_rx);
+ __ptr_ring_produce(q, xdpf);
}
processed++;
}
@@ -807,11 +824,15 @@ int cpu_map_generic_redirect(struct bpf_cpu_map_entry *rcpu,
__skb_pull(skb, skb->mac_len);
skb_set_redirected(skb, false);
+ dev_hold(skb->dev);
__ptr_set_bit(0, &skb);
ret = ptr_ring_produce(rcpu->queue, skb);
- if (ret < 0)
+ if (ret < 0) {
+ __ptr_clear_bit(0, &skb);
+ dev_put(skb->dev);
goto trace;
+ }
wake_up_process(rcpu->kthread);
trace:
diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
index 3f3fe2450fc6..69bc29d0a4d4 100644
--- a/kernel/bpf/crypto.c
+++ b/kernel/bpf/crypto.c
@@ -1,19 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 Meta, Inc */
#include <linux/bpf.h>
-#include <linux/bpf_crypto.h>
#include <linux/bpf_mem_alloc.h>
#include <linux/btf.h>
#include <linux/btf_ids.h>
#include <linux/filter.h>
-#include <linux/scatterlist.h>
#include <linux/skbuff.h>
-#include <crypto/skcipher.h>
-
-struct bpf_crypto_type_list {
- const struct bpf_crypto_type *type;
- struct list_head list;
-};
+#include <crypto/aes-cbc.h>
+#include <crypto/aes-ecb.h>
/* BPF crypto initialization parameters struct */
/**
@@ -36,94 +30,53 @@ struct bpf_crypto_params {
u32 authsize;
};
-static LIST_HEAD(bpf_crypto_types);
-static DECLARE_RWSEM(bpf_crypto_types_sem);
+enum bpf_crypto_algo_id {
+ BPF_ALGO_AES_CBC,
+ BPF_ALGO_AES_ECB,
+};
+
+static const struct {
+ const char *type_name;
+ const char *algo_name;
+ enum bpf_crypto_algo_id algo;
+} bpf_crypto_algos[] = {
+ { "skcipher", "cbc(aes)", BPF_ALGO_AES_CBC },
+ { "skcipher", "ecb(aes)", BPF_ALGO_AES_ECB },
+};
+
+static bool bpf_crypto_find_algo(const struct bpf_crypto_params *params,
+ enum bpf_crypto_algo_id *id_ret)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(bpf_crypto_algos); i++) {
+ if (strncmp(bpf_crypto_algos[i].type_name, params->type,
+ sizeof(params->type)) == 0 &&
+ strncmp(bpf_crypto_algos[i].algo_name, params->algo,
+ sizeof(params->algo)) == 0) {
+ *id_ret = bpf_crypto_algos[i].algo;
+ return true;
+ }
+ }
+ return false;
+}
/**
* struct bpf_crypto_ctx - refcounted BPF crypto context structure
- * @type: The pointer to bpf crypto type
- * @tfm: The pointer to instance of crypto API struct.
- * @siv_len: Size of IV and state storage for cipher
+ * @key: The crypto key
+ * @algo: The crypto algorithm ID
* @rcu: The RCU head used to free the crypto context with RCU safety.
* @usage: Object reference counter. When the refcount goes to 0, the
* memory is released back to the BPF allocator, which provides
* RCU safety.
*/
struct bpf_crypto_ctx {
- const struct bpf_crypto_type *type;
- void *tfm;
- u32 siv_len;
+ union {
+ struct aes_key aes;
+ } key;
+ enum bpf_crypto_algo_id algo;
struct rcu_head rcu;
refcount_t usage;
};
-int bpf_crypto_register_type(const struct bpf_crypto_type *type)
-{
- struct bpf_crypto_type_list *node;
- int err = -EBUSY;
-
- down_write(&bpf_crypto_types_sem);
- list_for_each_entry(node, &bpf_crypto_types, list) {
- if (!strcmp(node->type->name, type->name))
- goto unlock;
- }
-
- node = kmalloc_obj(*node);
- err = -ENOMEM;
- if (!node)
- goto unlock;
-
- node->type = type;
- list_add(&node->list, &bpf_crypto_types);
- err = 0;
-
-unlock:
- up_write(&bpf_crypto_types_sem);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(bpf_crypto_register_type);
-
-int bpf_crypto_unregister_type(const struct bpf_crypto_type *type)
-{
- struct bpf_crypto_type_list *node;
- int err = -ENOENT;
-
- down_write(&bpf_crypto_types_sem);
- list_for_each_entry(node, &bpf_crypto_types, list) {
- if (strcmp(node->type->name, type->name))
- continue;
-
- list_del(&node->list);
- kfree(node);
- err = 0;
- break;
- }
- up_write(&bpf_crypto_types_sem);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(bpf_crypto_unregister_type);
-
-static const struct bpf_crypto_type *bpf_crypto_get_type(const char *name)
-{
- const struct bpf_crypto_type *type = ERR_PTR(-ENOENT);
- struct bpf_crypto_type_list *node;
-
- down_read(&bpf_crypto_types_sem);
- list_for_each_entry(node, &bpf_crypto_types, list) {
- if (strcmp(node->type->name, name))
- continue;
-
- if (try_module_get(node->type->owner))
- type = node->type;
- break;
- }
- up_read(&bpf_crypto_types_sem);
-
- return type;
-}
-
__bpf_kfunc_start_defs();
/**
@@ -132,93 +85,70 @@ __bpf_kfunc_start_defs();
* Allocates a crypto context that can be used, acquired, and released by
* a BPF program. The crypto context returned by this function must either
* be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().
- * As crypto API functions use GFP_KERNEL allocations, this function can
- * only be used in sleepable BPF programs.
+ * As this uses a GFP_KERNEL allocation, this function can only be used in
+ * sleepable BPF programs.
*
* bpf_crypto_ctx_create() allocates memory for crypto context.
* It may return NULL if no memory is available.
* @params: pointer to struct bpf_crypto_params which contains all the
* details needed to initialise crypto context.
- * @params__sz: size of steuct bpf_crypto_params usef by bpf program
- * @err: integer to store error code when NULL is returned.
+ * @params__sz: size of struct bpf_crypto_params used by bpf program
+ * @err_ret: integer to store error code when NULL is returned.
*/
__bpf_kfunc struct bpf_crypto_ctx *
bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,
- int *err)
+ int *err_ret)
{
- const struct bpf_crypto_type *type;
struct bpf_crypto_ctx *ctx;
+ int err;
if (!params ||
params__sz != sizeof(struct bpf_crypto_params) ||
params->reserved[0] || params->reserved[1]) {
- *err = -EINVAL;
+ *err_ret = -EINVAL;
return NULL;
}
- type = bpf_crypto_get_type(params->type);
- if (IS_ERR(type)) {
- *err = PTR_ERR(type);
- return NULL;
- }
-
- if (!type->has_algo(params->algo)) {
- *err = -EOPNOTSUPP;
- goto err_module_put;
- }
-
- if (!!params->authsize ^ !!type->setauthsize) {
- *err = -EOPNOTSUPP;
- goto err_module_put;
- }
-
if (!params->key_len || params->key_len > sizeof(params->key)) {
- *err = -EINVAL;
- goto err_module_put;
+ *err_ret = -EINVAL;
+ return NULL;
}
ctx = kzalloc_obj(*ctx);
if (!ctx) {
- *err = -ENOMEM;
- goto err_module_put;
+ *err_ret = -ENOMEM;
+ return NULL;
}
- ctx->type = type;
- ctx->tfm = type->alloc_tfm(params->algo);
- if (IS_ERR(ctx->tfm)) {
- *err = PTR_ERR(ctx->tfm);
- goto err_free_ctx;
+ if (!bpf_crypto_find_algo(params, &ctx->algo)) {
+ err = -EOPNOTSUPP;
+ goto out;
}
- if (params->authsize) {
- *err = type->setauthsize(ctx->tfm, params->authsize);
- if (*err)
- goto err_free_tfm;
+ switch (ctx->algo) {
+ case BPF_ALGO_AES_CBC:
+ case BPF_ALGO_AES_ECB:
+ if (params->authsize)
+ err = -EOPNOTSUPP;
+ else
+ err = aes_preparekey(&ctx->key.aes, params->key,
+ params->key_len);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ err = -EOPNOTSUPP;
+ break;
}
- *err = type->setkey(ctx->tfm, params->key, params->key_len);
- if (*err)
- goto err_free_tfm;
-
- if (type->get_flags(ctx->tfm) & CRYPTO_TFM_NEED_KEY) {
- *err = -EINVAL;
- goto err_free_tfm;
+out:
+ if (err) {
+ kfree_sensitive(ctx);
+ *err_ret = err;
+ return NULL;
}
-
- ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);
-
refcount_set(&ctx->usage, 1);
-
+ *err_ret = 0;
return ctx;
-
-err_free_tfm:
- type->free_tfm(ctx->tfm);
-err_free_ctx:
- kfree(ctx);
-err_module_put:
- module_put(type->owner);
-
- return NULL;
}
static void crypto_free_cb(struct rcu_head *head)
@@ -226,9 +156,7 @@ static void crypto_free_cb(struct rcu_head *head)
struct bpf_crypto_ctx *ctx;
ctx = container_of(head, struct bpf_crypto_ctx, rcu);
- ctx->type->free_tfm(ctx->tfm);
- module_put(ctx->type->owner);
- kfree(ctx);
+ kfree_sensitive(ctx);
}
/**
@@ -268,27 +196,53 @@ __bpf_kfunc void bpf_crypto_ctx_release_dtor(void *ctx)
}
CFI_NOSEAL(bpf_crypto_ctx_release_dtor);
+static int bpf_aes_cbc_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,
+ u8 *iv, u32 iv_len,
+ const struct bpf_crypto_ctx *ctx, bool decrypt)
+{
+ if (iv_len != AES_BLOCK_SIZE)
+ return -EINVAL;
+ if (src_len % AES_BLOCK_SIZE || dst_len < src_len)
+ return -EINVAL;
+ if (decrypt)
+ aes_cbc_decrypt(dst, src, src_len, iv, &ctx->key.aes);
+ else
+ aes_cbc_encrypt(dst, src, src_len, iv, &ctx->key.aes);
+ return 0;
+}
+
+static int bpf_aes_ecb_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,
+ u8 *iv, u32 iv_len,
+ const struct bpf_crypto_ctx *ctx, bool decrypt)
+{
+ if (iv_len != 0)
+ return -EINVAL;
+ if (src_len % AES_BLOCK_SIZE || dst_len < src_len)
+ return -EINVAL;
+ if (decrypt)
+ aes_ecb_decrypt(dst, src, src_len, &ctx->key.aes);
+ else
+ aes_ecb_encrypt(dst, src, src_len, &ctx->key.aes);
+ return 0;
+}
+
static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
const struct bpf_dynptr_kern *src,
const struct bpf_dynptr_kern *dst,
- const struct bpf_dynptr_kern *siv,
+ const struct bpf_dynptr_kern *iv,
bool decrypt)
{
- u32 src_len, dst_len, siv_len;
+ u32 src_len, dst_len, iv_len;
const u8 *psrc;
u8 *pdst, *piv;
- int err;
if (__bpf_dynptr_is_rdonly(dst))
return -EINVAL;
- siv_len = siv ? __bpf_dynptr_size(siv) : 0;
+ iv_len = iv ? __bpf_dynptr_size(iv) : 0;
src_len = __bpf_dynptr_size(src);
dst_len = __bpf_dynptr_size(dst);
- if (!src_len || !dst_len || src_len > dst_len)
- return -EINVAL;
-
- if (siv_len != ctx->siv_len)
+ if (!src_len || !dst_len)
return -EINVAL;
psrc = __bpf_dynptr_data(src, src_len);
@@ -298,14 +252,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
if (!pdst)
return -EINVAL;
- piv = siv_len ? __bpf_dynptr_data_rw(siv, siv_len) : NULL;
- if (siv_len && !piv)
+ piv = iv_len ? __bpf_dynptr_data_rw(iv, iv_len) : NULL;
+ if (iv_len && !piv)
return -EINVAL;
- err = decrypt ? ctx->type->decrypt(ctx->tfm, psrc, pdst, src_len, piv)
- : ctx->type->encrypt(ctx->tfm, psrc, pdst, src_len, piv);
-
- return err;
+ switch (ctx->algo) {
+ case BPF_ALGO_AES_CBC:
+ return bpf_aes_cbc_crypt(pdst, dst_len, psrc, src_len, piv,
+ iv_len, ctx, decrypt);
+ case BPF_ALGO_AES_ECB:
+ return bpf_aes_ecb_crypt(pdst, dst_len, psrc, src_len, piv,
+ iv_len, ctx, decrypt);
+ default:
+ return -EINVAL;
+ }
}
/**
@@ -313,20 +273,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
* @ctx: The crypto context being used. The ctx must be a trusted pointer.
* @src: bpf_dynptr to the encrypted data. Must be a trusted pointer.
* @dst: bpf_dynptr to the buffer where to store the result. Must be a trusted pointer.
- * @siv__nullable: bpf_dynptr to IV data and state data to be used by decryptor. May be NULL.
+ * @iv__nullable: bpf_dynptr to the initialization vector. May be NULL.
*
* Decrypts provided buffer using IV data and the crypto context. Crypto context must be configured.
*/
__bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,
const struct bpf_dynptr *src,
const struct bpf_dynptr *dst,
- const struct bpf_dynptr *siv__nullable)
+ const struct bpf_dynptr *iv__nullable)
{
const struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;
const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
- const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+ const struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;
- return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, true);
+ return bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, true);
}
/**
@@ -334,20 +294,20 @@ __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,
* @ctx: The crypto context being used. The ctx must be a trusted pointer.
* @src: bpf_dynptr to the plain data. Must be a trusted pointer.
* @dst: bpf_dynptr to the buffer where to store the result. Must be a trusted pointer.
- * @siv__nullable: bpf_dynptr to IV data and state data to be used by decryptor. May be NULL.
+ * @iv__nullable: bpf_dynptr to the initialization vector. May be NULL.
*
* Encrypts provided buffer using IV data and the crypto context. Crypto context must be configured.
*/
__bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,
const struct bpf_dynptr *src,
const struct bpf_dynptr *dst,
- const struct bpf_dynptr *siv__nullable)
+ const struct bpf_dynptr *iv__nullable)
{
const struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;
const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
- const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+ const struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;
- return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, false);
+ return bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, false);
}
__bpf_kfunc_end_defs();
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index dc7b859e8bbf..1b8f0d7f8455 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -350,6 +350,7 @@ static int dev_map_bpf_prog_run(struct bpf_prog *xdp_prog,
xdp_convert_frame_to_buff(xdpf, &xdp);
xdp.txq = &txq;
+ rxq.mem.type = xdpf->mem_type;
xdp.rxq = &rxq;
act = bpf_prog_run_xdp(xdp_prog, &xdp);
diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index 8f64bfd9afee..b5204798231d 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -790,8 +790,9 @@ static void diag_print_insn_context(struct bpf_verifier_env *env, u32 insn_idx,
}
}
-static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const char *label,
- const char *fmt, ...)
+static __printf(4, 5) void bpf_diag_source(struct bpf_verifier_env *env,
+ u32 insn_idx, const char *label,
+ const char *fmt, ...)
{
struct bpf_diag_scratch *scratch;
struct bpf_linfo_source *source_lines;
@@ -956,6 +957,37 @@ const char *bpf_diag_reg_type_plain(struct bpf_verifier_env *env, enum bpf_reg_t
}
}
+const char *bpf_diag_arg_type_plain(enum bpf_arg_type type)
+{
+ switch (base_type(type)) {
+ case ARG_MEM_SIZE:
+ case ARG_CONST_MEM_SIZE:
+ return "an integer scalar length for this memory argument";
+ case ARG_PTR_TO_CTX:
+ return "the original program context pointer or preserve it before modifying registers";
+ case ARG_SCALAR:
+ case ARG_CONST_SCALAR:
+ case ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ return "an integer scalar value for this argument, not a pointer or resource object";
+ case ARG_PTR_TO_CONST_STR:
+ return "a constant string pointer that the verifier recognizes, such as a string stored in a read-only map value";
+ case ARG_PTR_TO_DYNPTR:
+ return "the address of a stack dynptr object, or use a const dynptr pointer returned by the verifier-supported path";
+ case ARG_PTR_TO_ALLOC_BTF_ID:
+ return "a pointer returned by the matching BPF object allocation path";
+ case ARG_PTR_TO_REFCOUNTED_KPTR:
+ return "an owning or non-owning pointer to a BPF-managed object containing a bpf_refcount field";
+ case ARG_PTR_TO_ITER:
+ return "the address of a stack iterator object for iterator new, next, and destroy calls";
+ case ARG_PTR_TO_IRQ_FLAG:
+ return "the same stack slot used by bpf_local_irq_save() or bpf_res_spin_lock_irqsave()";
+ case ARG_PTR_TO_CTX_OUT:
+ return "the attach hook's own output argument, loaded directly from the program context";
+ default:
+ return "a value with one of the accepted pointer or scalar types for this call";
+ }
+}
+
static const char *diag_arg_ordinal(int argno)
{
switch (argno) {
@@ -1083,16 +1115,17 @@ void bpf_diag_ctx_forbidden(struct bpf_verifier_env *env, u32 insn_idx,
const char *constraint, *context;
u32 depth;
- if (env->cur_state->active_rcu_locks)
- ctx_kind = BPF_DIAG_CONTEXT_RCU;
- else if (env->cur_state->active_preempt_locks)
- ctx_kind = BPF_DIAG_CONTEXT_PREEMPT;
- else if (env->cur_state->active_irq_id)
- ctx_kind = BPF_DIAG_CONTEXT_IRQ;
- else if (env->cur_state->active_locks)
- ctx_kind = BPF_DIAG_CONTEXT_LOCK;
- else
- ctx_kind = BPF_DIAG_CONTEXT_NONE;
+ ctx_kind = BPF_DIAG_CONTEXT_NONE;
+ if (env->cur_state->in_sleepable) {
+ if (env->cur_state->active_rcu_locks)
+ ctx_kind = BPF_DIAG_CONTEXT_RCU;
+ else if (env->cur_state->active_preempt_locks)
+ ctx_kind = BPF_DIAG_CONTEXT_PREEMPT;
+ else if (env->cur_state->active_irq_id)
+ ctx_kind = BPF_DIAG_CONTEXT_IRQ;
+ else if (env->cur_state->active_locks)
+ ctx_kind = BPF_DIAG_CONTEXT_LOCK;
+ }
depth = diag_context_depth(env, ctx_kind);
opts = (struct bpf_diag_history_opts) {
@@ -1531,8 +1564,7 @@ static void diag_record_mod(struct bpf_verifier_env *env, u32 insn_idx,
} else if (diag_mod_insn_origin(env, insn_idx, &target, &event.mod.origin)) {
event.mod.origin_valid = true;
}
- if (old_reg && new_reg &&
- (reason == BPF_DIAG_MOD_WRITE || reason == BPF_DIAG_MOD_SPILL) &&
+ if (old_reg && new_reg && reason == BPF_DIAG_MOD_WRITE &&
!memcmp(&event.mod.old, &event.mod.new, sizeof(event.mod.old)) &&
!event.mod.origin_valid &&
diag_mod_keeps_lineage(env, &event))
@@ -1564,9 +1596,9 @@ static struct bpf_reg_state *target_to_reg(struct bpf_verifier_env *env,
return NULL;
return &state->stack_arg_regs[target->stack_arg];
case BPF_DIAG_MOD_TARGET_STACK_SLOT:
- if (target->spi >= state->allocated_stack / BPF_REG_SIZE)
+ if (target->spi >= bpf_stack_nr_slots(state))
return NULL;
- return &state->stack[target->spi].spilled_ptr;
+ return &bpf_stack_slot(state, target->spi)->spilled_ptr;
default:
return NULL;
}
@@ -1582,7 +1614,7 @@ static bool reg_to_target(struct bpf_verifier_env *env, const struct bpf_reg_sta
for (frame = 0; frame <= vstate->curframe; frame++) {
struct bpf_func_state *state = vstate->frame[frame];
unsigned long start, end;
- u32 nslots = state->allocated_stack / BPF_REG_SIZE;
+ u32 nslots = bpf_stack_nr_slots(state);
int spi;
start = (unsigned long)state->regs;
@@ -1601,6 +1633,11 @@ static bool reg_to_target(struct bpf_verifier_env *env, const struct bpf_reg_sta
return true;
}
+ /*
+ * Map the pointer back to its slot by address, which relies on
+ * the slots forming one contiguous array as bpf_stack_slot()
+ * indexes it.
+ */
start = (unsigned long)state->stack;
end = (unsigned long)(state->stack + nslots);
if (nslots && addr >= start && addr < end) {
diff --git a/kernel/bpf/diagnostics.h b/kernel/bpf/diagnostics.h
index d1b79945008a..a4102fb049ec 100644
--- a/kernel/bpf/diagnostics.h
+++ b/kernel/bpf/diagnostics.h
@@ -51,6 +51,7 @@ const char *bpf_diag_vfmt(struct bpf_verifier_env *env, const char *fmt, va_list
const char *bpf_diag_fmt(struct bpf_verifier_env *env, const char *fmt, ...) __printf(2, 3);
const char *bpf_diag_fmt_btf_type(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id);
const char *bpf_diag_reg_type_plain(struct bpf_verifier_env *env, enum bpf_reg_type type);
+const char *bpf_diag_arg_type_plain(enum bpf_arg_type type);
u64 bpf_diag_event_log_save(struct bpf_verifier_env *env);
void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos);
u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state);
diff --git a/kernel/bpf/disasm.c b/kernel/bpf/disasm.c
index 3ce8d74b0e40..889fc074496f 100644
--- a/kernel/bpf/disasm.c
+++ b/kernel/bpf/disasm.c
@@ -350,7 +350,10 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
if (opcode == BPF_CALL) {
char tmp[64];
- if (insn->src_reg == BPF_PSEUDO_CALL) {
+ if (BPF_SRC(insn->code) == BPF_X) {
+ verbose(cbs->private_data, "(%02x) callx r%d",
+ insn->code, insn->dst_reg);
+ } else if (insn->src_reg == BPF_PSEUDO_CALL) {
verbose(cbs->private_data, "(%02x) call pc%s",
insn->code,
__func_get_name(cbs, insn,
@@ -372,11 +375,16 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
insn->src_reg == BPF_MAY_GOTO) {
verbose(cbs->private_data, "(%02x) may_goto pc%+d",
insn->code, insn->off);
+ } else if (insn->code == (BPF_JMP | BPF_JCOND) &&
+ insn->src_reg == BPF_UNWIND_GOTO) {
+ verbose(cbs->private_data, "(%02x) unwind_goto pc%+d",
+ insn->code, insn->off);
} else if (insn->code == (BPF_JMP32 | BPF_JA)) {
verbose(cbs->private_data, "(%02x) gotol pc%+d",
insn->code, insn->imm);
} else if (insn->code == (BPF_JMP | BPF_EXIT)) {
- verbose(cbs->private_data, "(%02x) exit", insn->code);
+ verbose(cbs->private_data, "(%02x) %s", insn->code,
+ insn->src_reg == BPF_UNWIND_EXIT ? "unwind_exit" : "exit");
} else if (BPF_SRC(insn->code) == BPF_X) {
verbose(cbs->private_data,
"(%02x) if %c%d %s %c%d goto pc%+d",
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index d6f83521fc78..f062ef7dd943 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -5,8 +5,6 @@
#include <linux/bpf_verifier.h>
#include <linux/filter.h>
#include <linux/vmalloc.h>
-#include <linux/bsearch.h>
-#include <linux/sort.h>
#include <linux/perf_event.h>
#include <linux/sched/signal.h>
#include <net/xdp.h>
@@ -46,6 +44,31 @@ static bool is_addr_space_cast32(struct bpf_prog *prog, const struct bpf_insn *i
return false;
}
+/*
+ * The insn accesses arena through a number. JITs add the base of arena
+ * to the register as it is, so the access goes through the low 32 bits of
+ * the number in BPF_REG_AX. Registers of the program are not changed: the
+ * number may be compared or stored later, and on another path the register
+ * may be PTR_TO_ARENA.
+ *
+ * Constant blinding leaves insns that use BPF_REG_AX alone, so the immediate
+ * of st through a number is not blinded.
+ */
+static int arena_scalar_access(const struct bpf_insn *insn, struct bpf_insn *buf)
+{
+ bool load = BPF_CLASS(insn->code) == BPF_LDX || bpf_atomic_is_load_acq(insn);
+ struct bpf_insn *patch = buf;
+
+ *patch++ = BPF_MOV32_REG(BPF_REG_AX, load ? insn->src_reg : insn->dst_reg);
+ *patch = *insn;
+ if (load)
+ patch->src_reg = BPF_REG_AX;
+ else
+ patch->dst_reg = BPF_REG_AX;
+ patch++;
+ return patch - buf;
+}
+
/* Return the regno defined by the insn, or -1. */
static int insn_def_regno(const struct bpf_insn *insn)
{
@@ -117,73 +140,26 @@ int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn)
return dst_reg;
}
-static int kfunc_desc_cmp_by_imm_off(const void *a, const void *b)
-{
- const struct bpf_kfunc_desc *d0 = a;
- const struct bpf_kfunc_desc *d1 = b;
-
- if (d0->imm != d1->imm)
- return d0->imm < d1->imm ? -1 : 1;
- if (d0->offset != d1->offset)
- return d0->offset < d1->offset ? -1 : 1;
- return 0;
-}
-
const struct btf_func_model *
bpf_jit_find_kfunc_model(const struct bpf_prog *prog,
const struct bpf_insn *insn)
{
- const struct bpf_kfunc_desc desc = {
- .imm = insn->imm,
- .offset = insn->off,
- };
const struct bpf_kfunc_desc *res;
struct bpf_kfunc_desc_tab *tab;
tab = prog->aux->kfunc_tab;
- res = bsearch(&desc, tab->descs, tab->nr_descs,
- sizeof(tab->descs[0]), kfunc_desc_cmp_by_imm_off);
-
- return res ? &res->func_model : NULL;
-}
-
-static int set_kfunc_desc_imm(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc)
-{
- unsigned long call_imm;
+ if (insn->off < 0 || insn->off >= tab->nr_descs)
+ return NULL;
+ res = &tab->descs[insn->off];
if (bpf_jit_supports_far_kfunc_call()) {
- call_imm = desc->func_id;
- } else {
- call_imm = BPF_CALL_IMM(desc->addr);
- /* Check whether the relative offset overflows desc->imm */
- if ((unsigned long)(s32)call_imm != call_imm) {
- verbose(env, "address of kernel func_id %u is out of range\n",
- desc->func_id);
- return -EINVAL;
- }
- }
- desc->imm = call_imm;
- return 0;
-}
-
-static int sort_kfunc_descs_by_imm_off(struct bpf_verifier_env *env)
-{
- struct bpf_kfunc_desc_tab *tab;
- int i, err;
-
- tab = env->prog->aux->kfunc_tab;
- if (!tab)
- return 0;
-
- for (i = 0; i < tab->nr_descs; i++) {
- err = set_kfunc_desc_imm(env, &tab->descs[i]);
- if (err)
- return err;
+ if (res->func_id != insn->imm)
+ return NULL;
+ } else if ((s32)BPF_CALL_IMM(res->addr) != insn->imm) {
+ return NULL;
}
- sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
- kfunc_desc_cmp_by_imm_off, NULL);
- return 0;
+ return &res->func_model;
}
static int add_kfunc_in_insns(struct bpf_verifier_env *env,
@@ -214,12 +190,19 @@ static int get_callee_stack_depth(struct bpf_verifier_env *env,
}
#endif
+enum bpf_patch_mode {
+ BPF_PATCH_KEEP_TARGET, /* targets of 'off' keep naming 'off', the first new insn */
+ BPF_PATCH_MOVE_TARGET, /* targets of 'off' follow the original insn to 'off + len - 1' */
+};
+
/* single env->prog->insni[off] instruction was replaced with the range
* insni[off, off + cnt). Adjust corresponding insn_aux_data by copying
* [0, off) and [off, end) to new locations, so the patched range stays zero
*/
static void adjust_insn_aux_data(struct bpf_verifier_env *env,
- struct bpf_prog *new_prog, u32 off, u32 cnt)
+ struct bpf_prog *new_prog, u32 off, u32 cnt,
+ struct bpf_insn *original_insn,
+ enum bpf_patch_mode mode)
{
struct bpf_insn_aux_data *data = env->insn_aux_data;
struct bpf_insn *insn = new_prog->insnsi;
@@ -233,8 +216,15 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
*/
data[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) >= 0;
- if (cnt == 1)
+ if (cnt == 1) {
+ /*
+ * A non-memory accessing insn could have been replaced by a
+ * memory accessing insn, systematically mark it for non-stack
+ * access
+ */
+ data[off].non_stack_access = bpf_is_mem_insn(insn + off);
return;
+ }
prog_len = new_prog->len;
env->insn_aux_data_len = prog_len;
@@ -245,16 +235,39 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
/* Expand insni[off]'s seen count to the patched range. */
data[i].seen = old_seen;
data[i].zext_dst = bpf_insn_def32(new_prog, insn + i) >= 0;
+ /*
+ * In BPF_PATCH_MOVE_TARGET mode the original insn is the last
+ * slot by construction.
+ */
+ if (mode == BPF_PATCH_KEEP_TARGET &&
+ !memcmp(insn + i, original_insn, sizeof(struct bpf_insn))) {
+ data[i].non_stack_access =
+ data[off + cnt - 1].non_stack_access;
+ data[off + cnt - 1].non_stack_access = false;
+ } else if (bpf_is_mem_insn(insn + i)) {
+ data[i].non_stack_access = true;
+ }
}
/*
+ * Last slot instruction could be a newly generated
+ * BPF_ST/BPF_LDX/BPF_STX, systematically mark it for non-stack access
+ * if it is not the original instruction, otherwise keep the
+ * original marking
+ */
+ if (bpf_is_mem_insn(insn + off + cnt - 1) &&
+ memcmp(insn + off + cnt - 1, original_insn, sizeof(struct bpf_insn)))
+ data[off + cnt - 1].non_stack_access = true;
+
+ /*
* The indirect_target flag of the original instruction was moved to the last of the
* new instructions by the above memmove and memset, but the indirect jump target is
* actually the first instruction, so move it back. This also matches with the behavior
* of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new
- * instruction.
+ * instruction. For BPF_PATCH_MOVE_TARGET the original instruction is the last one,
+ * so the flag already sits where needed.
*/
- if (data[off + cnt - 1].indirect_target) {
+ if (mode == BPF_PATCH_KEEP_TARGET && data[off + cnt - 1].indirect_target) {
data[off].indirect_target = 1;
data[off + cnt - 1].indirect_target = 0;
}
@@ -274,7 +287,7 @@ static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len
}
}
-static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 off, u32 len)
+static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 first, u32 len)
{
int i;
@@ -282,7 +295,7 @@ static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 off, u32 len)
return;
for (i = 0; i < env->insn_array_map_cnt; i++)
- bpf_insn_array_adjust(env->insn_array_maps[i], off, len);
+ bpf_insn_array_adjust(env->insn_array_maps[i], first, len);
}
static void adjust_insn_arrays_after_remove(struct bpf_verifier_env *env, u32 off, u32 len)
@@ -293,7 +306,7 @@ static void adjust_insn_arrays_after_remove(struct bpf_verifier_env *env, u32 of
bpf_insn_array_adjust_after_remove(env->insn_array_maps[i], off, len);
}
-static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)
+static void adjust_poke_descs(struct bpf_prog *prog, u32 first, u32 len)
{
struct bpf_jit_poke_descriptor *tab = prog->aux->poke_tab;
int i, sz = prog->aux->size_poke_tab;
@@ -301,7 +314,7 @@ static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)
for (i = 0; i < sz; i++) {
desc = &tab[i];
- if (desc->insn_idx <= off)
+ if (desc->insn_idx < first)
continue;
desc->insn_idx += len - 1;
}
@@ -320,11 +333,15 @@ static bool bpf_rewrite_must_abort(void)
return false;
}
-struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
- const struct bpf_insn *patch, u32 len)
+static struct bpf_prog *__bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
+ const struct bpf_insn *patch, u32 len,
+ enum bpf_patch_mode mode)
{
struct bpf_prog *new_prog;
struct bpf_insn_aux_data *new_data = NULL;
+ /* The first slot whose targets move. */
+ u32 first = mode == BPF_PATCH_MOVE_TARGET ? off : off + 1;
+ struct bpf_insn original_insn;
if (bpf_rewrite_must_abort())
return NULL;
@@ -340,6 +357,7 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
env->insn_aux_data = new_data;
}
+ memcpy(&original_insn, env->prog->insnsi + off, sizeof(struct bpf_insn));
new_prog = bpf_patch_insn_single(env->prog, off, patch, len);
if (IS_ERR(new_prog)) {
if (PTR_ERR(new_prog) == -ERANGE)
@@ -348,13 +366,58 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
env->insn_aux_data[off].orig_idx);
return NULL;
}
- adjust_insn_aux_data(env, new_prog, off, len);
+ adjust_insn_aux_data(env, new_prog, off, len, &original_insn, mode);
adjust_subprog_starts(env, off, len);
- adjust_insn_arrays(env, off, len);
- adjust_poke_descs(new_prog, off, len);
+ adjust_insn_arrays(env, first, len);
+ bpf_adjust_func_ptrs(env, off, len);
+ adjust_poke_descs(new_prog, first, len);
return new_prog;
}
+struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
+ const struct bpf_insn *patch, u32 len)
+{
+ return __bpf_patch_insn_data(env, off, patch, len, BPF_PATCH_KEEP_TARGET);
+}
+
+/*
+ * insn was moved down by delta insns inside its own patch. Operands relative
+ * to the pc that point in front of the old position did not move with it.
+ */
+static int bpf_adj_moved_insn(struct bpf_insn *insn, u32 delta)
+{
+ u8 class = BPF_CLASS(insn->code), op = BPF_OP(insn->code);
+ s64 off = insn->imm, off_min = S32_MIN;
+ bool is_imm = true;
+
+ if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn)) {
+ /* subprog that started at the old position starts with the patch */
+ if (off >= 0)
+ return 0;
+ } else if ((class == BPF_JMP || class == BPF_JMP32) &&
+ op != BPF_CALL && op != BPF_EXIT) {
+ if (insn->code != (BPF_JMP32 | BPF_JA)) {
+ off = insn->off;
+ off_min = S16_MIN;
+ is_imm = false;
+ }
+ /* jump to itself stays */
+ if (off >= -1)
+ return 0;
+ } else {
+ return 0;
+ }
+
+ off -= delta;
+ if (off < off_min)
+ return -ERANGE;
+ if (is_imm)
+ insn->imm = off;
+ else
+ insn->off = off;
+ return 0;
+}
+
/*
* For all jmp insns in a given 'prog' that point to 'tgt_idx' insn adjust the
* jump offset by 'delta'.
@@ -440,6 +503,9 @@ static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env,
* in adjust_btf_func() - no need to adjust
*/
}
+ /* The exception callback is live, so it cannot sit inside the removed range. */
+ if (env->exception_callback_subprog >= j)
+ env->exception_callback_subprog -= j - i;
} else {
/* convert i from "first prog to remove" to "first to adjust" */
if (env->subprog_info[i].start == off)
@@ -517,9 +583,7 @@ static int bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off,
return 0;
}
-/*
- * Clean up dynamically allocated fields of aux data for instructions [start, ...]
- */
+/* Clean up dynamically allocated fields of aux data for instructions [start, ...] */
void bpf_clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len)
{
struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
@@ -527,9 +591,10 @@ void bpf_clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len)
int i;
for (i = start; i < end; i++) {
- if (aux_data[i].jt) {
- kvfree(aux_data[i].jt);
- aux_data[i].jt = NULL;
+ if (aux_data[i].loop) {
+ kvfree(aux_data[i].loop->exits);
+ kvfree(aux_data[i].loop);
+ aux_data[i].loop = NULL;
}
}
}
@@ -552,6 +617,9 @@ static int verifier_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt)
if (err)
return err;
+ /* before subprogs are adjusted, since it looks at them */
+ bpf_adjust_func_ptrs_after_remove(env, off, cnt);
+
err = adjust_subprog_starts_after_remove(env, off, cnt);
if (err)
return err;
@@ -590,6 +658,7 @@ void bpf_opt_hard_wire_dead_code_branches(struct bpf_verifier_env *env)
{
struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
struct bpf_insn ja = BPF_JMP_IMM(BPF_JA, 0, 0, 0);
+ struct bpf_insn trap = BPF_JMP_IMM(BPF_JA, 0, 0, -1);
struct bpf_insn *insn = env->prog->insnsi;
const int insn_cnt = env->prog->len;
int i;
@@ -598,6 +667,20 @@ void bpf_opt_hard_wire_dead_code_branches(struct bpf_verifier_env *env)
if (!bpf_insn_is_cond_jump(insn->code))
continue;
+ if (bpf_is_unwind_goto(insn) && aux_data[i + 1 + insn->off].seen) {
+ /*
+ * The callee never exits. Its unwind_exit needs
+ * unwind_goto all the same, and the cpu predicts that it
+ * returns to what follows. Don't let the removal of dead
+ * code put something else there.
+ */
+ if (!aux_data[i + 1].seen) {
+ memcpy(insn + 1, &trap, sizeof(trap));
+ aux_data[i + 1].seen = env->pass_cnt;
+ }
+ continue;
+ }
+
if (!aux_data[i + 1].seen)
ja.off = insn->off;
else if (!aux_data[i + 1 + insn->off].seen)
@@ -612,12 +695,41 @@ void bpf_opt_hard_wire_dead_code_branches(struct bpf_verifier_env *env)
}
}
+/*
+ * The address of a function might be taken by the code that is not dead,
+ * while the function itself is dead code, since nothing calls it. Keep the first
+ * instruction of such function, so that the address remains valid and differs
+ * from the addresses of other functions. Make it a trap like
+ * sanitize_dead_code() does.
+ */
+static void keep_funcs_with_addr_taken(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
+ struct bpf_insn trap = BPF_JMP_IMM(BPF_JA, 0, 0, -1);
+ struct bpf_insn *insn = env->prog->insnsi;
+ int insn_cnt = env->prog->len;
+ int i, t;
+
+ for (i = 0; i < insn_cnt; i++) {
+ if (!aux_data[i].seen || !bpf_pseudo_func(insn + i))
+ continue;
+ t = i + insn[i].imm + 1;
+ if (aux_data[t].seen)
+ continue;
+ memcpy(insn + t, &trap, sizeof(trap));
+ aux_data[t].zext_dst = false;
+ aux_data[t].seen = env->pass_cnt;
+ }
+}
+
int bpf_opt_remove_dead_code(struct bpf_verifier_env *env)
{
struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
int insn_cnt = env->prog->len;
int i, err;
+ keep_funcs_with_addr_taken(env);
+
for (i = 0; i < insn_cnt; i++) {
int j;
@@ -638,6 +750,7 @@ int bpf_opt_remove_dead_code(struct bpf_verifier_env *env)
int bpf_opt_remove_nops(struct bpf_verifier_env *env)
{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
struct bpf_insn *insn = env->prog->insnsi;
int insn_cnt = env->prog->len;
bool is_may_goto_0, is_ja;
@@ -649,6 +762,8 @@ int bpf_opt_remove_nops(struct bpf_verifier_env *env)
if (!is_may_goto_0 && !is_ja)
continue;
+ if (aux[i].indirect_target)
+ continue;
err = verifier_remove_insns(env, i, 1);
if (err)
@@ -758,11 +873,61 @@ apply_patch_buffer:
return 0;
}
+/*
+ * The prologue takes the context from R1, and it can clobber R2-R5. An
+ * extension gets the arguments of the function that it replaces.
+ */
+static bool ext_prog_takes_prologue(const struct bpf_verifier_env *env)
+{
+ const struct bpf_subprog_info *sub = &env->subprog_info[0];
+ int i, cnt = min_t(int, sub->arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS);
+
+ if (!cnt || sub->args[0].arg_type != ARG_PTR_TO_CTX)
+ return false;
+
+ for (i = 1; i < cnt; i++)
+ if (sub->args[i].arg_type != ARG_SCALAR)
+ return false;
+
+ return true;
+}
+
/* convert load instructions that access fields of a context type into a
* sequence of instructions that access fields of the underlying structure:
* struct __sk_buff -> struct sk_buff
* struct bpf_sock_ops -> struct sock
*/
+/*
+ * A load through an __arena pointer member of a kernel struct reads the kernel
+ * address of arena memory. Append the conversion to the arena pointer form,
+ * (u32)(kaddr - kern_vm_start), that the struct_ops trampoline applies to
+ * __arena arguments. A __nullable member keeps NULL as NULL, tested on the
+ * full kernel address, and so does a probed load, which reads as 0 when it
+ * faults. Returns the number of insns in @buf, 0 when the insn loads no such
+ * member, or an error when it does so on some paths only.
+ */
+static int arena_member_fixup(struct bpf_verifier_env *env, int idx, struct bpf_insn *insn,
+ struct bpf_insn *buf)
+{
+ struct bpf_insn_aux_data *aux = &env->insn_aux_data[idx];
+ int cnt = 0;
+ u32 base;
+
+ if (!aux->arena_member)
+ return 0;
+ if (aux->non_arena_load) {
+ verbose(env, "an insn loads an arena pointer member on one path and something else on another\n");
+ return -EINVAL;
+ }
+
+ base = bpf_arena_get_kern_vm_start(env->prog->aux->arena);
+ buf[cnt++] = *insn;
+ if (aux->arena_member_nullable || BPF_MODE(insn->code) == BPF_PROBE_MEM)
+ buf[cnt++] = BPF_JMP_IMM(BPF_JEQ, insn->dst_reg, 0, 1);
+ buf[cnt++] = BPF_ALU32_IMM(BPF_SUB, insn->dst_reg, (s32)base);
+ return cnt;
+}
+
int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
{
struct bpf_subprog_info *subprogs = env->subprog_info;
@@ -791,7 +956,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
insn_buf[cnt++] = BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_1,
-subprogs[0].stack_depth);
insn_buf[cnt++] = env->prog->insnsi[0];
- new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);
+ new_prog = __bpf_patch_insn_data(env, 0, insn_buf, cnt,
+ BPF_PATCH_MOVE_TARGET);
if (!new_prog)
return -ENOMEM;
env->prog = new_prog;
@@ -814,7 +980,20 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
verifier_bug(env, "prologue is too long");
return -EFAULT;
} else if (cnt) {
- new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);
+ if (env->prog->type == BPF_PROG_TYPE_EXT &&
+ !ext_prog_takes_prologue(env)) {
+ verbose(env,
+ "Extension writing packet data must take ctx and scalars only\n");
+ return -EINVAL;
+ }
+ if (env->prog->type == BPF_PROG_TYPE_EXT &&
+ bpf_check_ext_prologue(&env->log,
+ env->prog->aux->dst_prog,
+ env->prog->aux->attach_btf_id))
+ return -EINVAL;
+ env->prog->aux->has_prologue = true;
+ new_prog = __bpf_patch_insn_data(env, 0, insn_buf, cnt,
+ BPF_PATCH_MOVE_TARGET);
if (!new_prog)
return -ENOMEM;
@@ -845,7 +1024,13 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
struct bpf_insn *patch = insn_buf;
*patch++ = BPF_ST_NOSPEC();
- *patch++ = *insn;
+ *patch = *insn;
+ ret = bpf_adj_moved_insn(patch++, 1);
+ if (ret) {
+ verbose(env, "insn %d cannot be patched due to 16-bit range\n",
+ env->insn_aux_data[i + delta].orig_idx);
+ return ret;
+ }
cnt = patch - insn_buf;
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
if (!new_prog)
@@ -887,6 +1072,21 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
insn->code = BPF_STX | BPF_PROBE_ATOMIC | BPF_SIZE(insn->code);
env->prog->aux->num_exentries++;
continue;
+ } else if (insn->code == (BPF_STX | BPF_ATOMIC | BPF_DW) &&
+ insn->imm == BPF_LOAD_ACQ &&
+ env->insn_aux_data[i + delta].arena_member) {
+ /* an acquire load of an __arena member is rewritten like a plain load */
+ cnt = arena_member_fixup(env, i + delta, insn, insn_buf);
+ if (cnt < 0)
+ return cnt;
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ if (!new_prog)
+ return -ENOMEM;
+
+ delta += cnt - 1;
+ env->prog = new_prog;
+ insn = new_prog->insnsi + i + delta;
+ continue;
} else if (insn->code == (BPF_JMP | BPF_EXIT) &&
epilogue_cnt &&
i + delta < subprogs[1].start) {
@@ -978,11 +1178,23 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
insn->code = BPF_LDX | BPF_PROBE_MEMSX |
BPF_SIZE(insn->code);
env->prog->aux->num_exentries++;
- continue;
- }
- if (verifier_bug_if(bpf_may_fault_on_deref(ptr_type), env,
- "access to a fault prone pointer is not rewritten as a probed one"))
+ } else if (verifier_bug_if(bpf_may_fault_on_deref(ptr_type), env,
+ "access to a fault prone pointer is not rewritten as a probed one")) {
return -EFAULT;
+ }
+
+ cnt = arena_member_fixup(env, i + delta, insn, insn_buf);
+ if (cnt < 0)
+ return cnt;
+ if (!cnt)
+ continue;
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ if (!new_prog)
+ return -ENOMEM;
+
+ delta += cnt - 1;
+ env->prog = new_prog;
+ insn = new_prog->insnsi + i + delta;
continue;
}
@@ -1088,6 +1300,59 @@ static void bpf_restore_subprog_starts(struct bpf_verifier_env *env, u32 *orig_s
env->subprog_info[env->subprog_cnt].start = env->prog->len;
}
+/*
+ * Replace the offsets of functions with their addresses in the maps of
+ * the program, see resolve_func_ptrs() in verifier.c. The program must be
+ * the only user of such map. From now on no other program can use it, see
+ * bpf_map_claim().
+ */
+static int resolve_func_ptrs(struct bpf_verifier_env *env, struct bpf_prog *prog,
+ struct bpf_prog **func)
+{
+ unsigned long me = (unsigned long)prog->aux;
+ struct bpf_func_ptr *ptr;
+ int i, err, subprog;
+ u64 addr, new, *slot;
+
+ for (i = 0; i < env->func_ptr_cnt; i++) {
+ ptr = &env->func_ptrs[i];
+
+ /* pointers are sorted by map */
+ if ((!i || ptr->map != ptr[-1].map) &&
+ cmpxchg(&ptr->map->user, me, me | BPF_MAP_USER_PATCHED) != me) {
+ verbose(env, "map '%s' is used by another program\n", ptr->map->name);
+ return -EBUSY;
+ }
+
+ /* it's the address of the value of the map whatever the offset is */
+ err = ptr->map->ops->map_direct_value_addr(ptr->map, &addr, 0);
+ if (verifier_bug_if(err, env, "no value of map '%s'", ptr->map->name))
+ return -EFAULT;
+ slot = (u64 *)(unsigned long)(addr + ptr->map_off);
+
+ /*
+ * The function that is dead code is removed and the pointer is
+ * NULL. The program may read it, but callx of it is dead code
+ * too, see check_func_callx().
+ */
+ new = 0;
+ if (ptr->xlated_off != BPF_FUNC_PTR_DELETED) {
+ subprog = bpf_find_subprog(env, ptr->xlated_off);
+ if (verifier_bug_if(!func || subprog <= 0, env, "no function at insn %u",
+ ptr->xlated_off))
+ return -EFAULT;
+ new = (unsigned long)func[subprog]->bpf_func;
+ }
+
+ /* the map is frozen and nothing else uses it */
+ if (verifier_bug_if(*slot != (u64)ptr->orig_off * sizeof(struct bpf_insn), env,
+ "map '%s' offset %u changed", ptr->map->name, ptr->map_off))
+ return -EFAULT;
+ WRITE_ONCE(*slot, new);
+ }
+ return 0;
+}
+
static int jit_subprogs(struct bpf_verifier_env *env)
{
struct bpf_prog *prog = env->prog, **func, *tmp;
@@ -1198,6 +1463,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
func[i]->aux->jited_linfo = prog->aux->jited_linfo;
func[i]->aux->linfo_idx = env->subprog_info[i].linfo_idx;
func[i]->aux->arena = prog->aux->arena;
+ func[i]->aux->uses_arena_insns = bpf_prog_has_arena_insns(func[i]);
func[i]->aux->used_maps = env->used_maps;
func[i]->aux->used_map_cnt = env->used_map_cnt;
num_exentries = 0;
@@ -1216,12 +1482,15 @@ static int jit_subprogs(struct bpf_verifier_env *env)
if (BPF_CLASS(insn->code) == BPF_STX &&
BPF_MODE(insn->code) == BPF_PROBE_ATOMIC)
num_exentries++;
+ if (bpf_is_unwind_goto(insn))
+ num_exentries++;
}
func[i]->aux->num_exentries = num_exentries;
func[i]->aux->tail_call_reachable = env->subprog_info[i].tail_call_reachable;
func[i]->aux->exception_cb = env->subprog_info[i].is_exception_cb;
func[i]->aux->changes_pkt_data = env->subprog_info[i].changes_pkt_data;
func[i]->aux->might_sleep = env->subprog_info[i].might_sleep;
+ func[i]->aux->can_unwind = env->subprog_info[i].can_unwind;
func[i]->aux->token = prog->aux->token;
if (!i)
func[i]->aux->exception_boundary = env->seen_exception;
@@ -1278,6 +1547,11 @@ static int jit_subprogs(struct bpf_verifier_env *env)
cond_resched();
}
+ /* the addresses of all functions are final */
+ err = resolve_func_ptrs(env, prog, func);
+ if (err)
+ goto out_free;
+
/*
* Cleanup func[i]->aux fields which aren't required
* or can become invalid in future
@@ -1364,8 +1638,9 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env)
struct bpf_prog *prog, *orig_prog;
u32 *orig_subprog_starts;
+ /* all functions that the maps point to are removed as dead code */
if (env->subprog_cnt <= 1)
- return 0;
+ return resolve_func_ptrs(env, env->prog, NULL);
prog = orig_prog = env->prog;
if (bpf_prog_need_blind(prog)) {
@@ -1522,11 +1797,37 @@ static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *pat
env->prog = prog;
info[cnt + 1].start = info[cnt].start;
info[cnt].start = prog->len - len + 1;
+ /* The patch ends with the exit. */
+ info[cnt].exit_idx = prog->len - 1;
env->subprog_cnt++;
env->hidden_subprog_cnt++;
return 0;
}
+/*
+ * Expand may_goto: load the count from the stack, jump to the target of
+ * may_goto when it is zero, then the tail that updates the count.
+ * Use gotol when the target is too far for 16-bit offset of a conditional jump.
+ */
+static int may_goto_expand(struct bpf_insn *insn_buf, int off, int stack_off,
+ const struct bpf_insn *tail, int tail_cnt)
+{
+ int cnt = 0;
+
+ /* Forward jump has to step over the tail */
+ off = off >= 0 ? off + tail_cnt : off - 1;
+
+ insn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, BPF_REG_AX, BPF_REG_10, stack_off);
+ if (off == (s16)off) {
+ insn_buf[cnt++] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, off);
+ } else {
+ insn_buf[cnt++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_AX, 0, 1);
+ insn_buf[cnt++] = BPF_JMP32_A(off >= 0 ? off : off - 1);
+ }
+ memcpy(insn_buf + cnt, tail, tail_cnt * sizeof(*tail));
+ return cnt + tail_cnt;
+}
+
/* Do various post-verification rewrites in a single program pass.
* These rewrites simplify JIT and interpreter implementations.
*/
@@ -1579,6 +1880,20 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
/* Convert BPF_CLASS(insn->code) == BPF_ALU64 to 32-bit ALU */
insn->code = BPF_ALU | BPF_OP(insn->code) | BPF_SRC(insn->code);
+ if (env->insn_aux_data[i + delta].arena_scalar) {
+ cnt = arena_scalar_access(insn, insn_buf);
+
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ if (!new_prog)
+ return -ENOMEM;
+
+ delta += cnt - 1;
+ prog = new_prog;
+ env->prog = prog;
+ insn = prog->insnsi + i + delta;
+ goto next_insn;
+ }
+
/* Make sdiv/smod divide-by-minus-one exceptions impossible. */
if ((insn->code == (BPF_ALU64 | BPF_MOD | BPF_K) ||
insn->code == (BPF_ALU64 | BPF_DIV | BPF_K) ||
@@ -1804,6 +2119,18 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
if (bpf_is_may_goto_insn(insn) && bpf_jit_supports_timed_may_goto()) {
int stack_off_cnt = -stack_depth - 16;
+ /*
+ * AX is used as an argument to pass in stack_off_cnt
+ * (to add to r10/fp), and also as the return value of
+ * the call to arch_bpf_timed_may_goto.
+ */
+ struct bpf_insn tail[] = {
+ BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_AX, 0, 2),
+ BPF_MOV64_IMM(BPF_REG_AX, stack_off_cnt),
+ BPF_EMIT_CALL(arch_bpf_timed_may_goto),
+ BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off_cnt),
+ };
/*
* Two 8 byte slots, depth-16 stores the count, and
@@ -1820,22 +2147,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
* after subtraction, rinse and repeat.
*/
stack_depth_extra = 16;
- insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_AX, BPF_REG_10, stack_off_cnt);
- if (insn->off >= 0)
- insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off + 5);
- else
- insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off - 1);
- insn_buf[2] = BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1);
- insn_buf[3] = BPF_JMP_IMM(BPF_JNE, BPF_REG_AX, 0, 2);
- /*
- * AX is used as an argument to pass in stack_off_cnt
- * (to add to r10/fp), and also as the return value of
- * the call to arch_bpf_timed_may_goto.
- */
- insn_buf[4] = BPF_MOV64_IMM(BPF_REG_AX, stack_off_cnt);
- insn_buf[5] = BPF_EMIT_CALL(arch_bpf_timed_may_goto);
- insn_buf[6] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off_cnt);
- cnt = 7;
+ cnt = may_goto_expand(insn_buf, insn->off, stack_off_cnt,
+ tail, ARRAY_SIZE(tail));
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
if (!new_prog)
@@ -1847,16 +2160,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
goto next_insn;
} else if (bpf_is_may_goto_insn(insn)) {
int stack_off = -stack_depth - 8;
+ struct bpf_insn tail[] = {
+ BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1),
+ BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off),
+ };
stack_depth_extra = 8;
- insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_AX, BPF_REG_10, stack_off);
- if (insn->off >= 0)
- insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off + 2);
- else
- insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off - 1);
- insn_buf[2] = BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1);
- insn_buf[3] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off);
- cnt = 4;
+ cnt = may_goto_expand(insn_buf, insn->off, stack_off,
+ tail, ARRAY_SIZE(tail));
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
if (!new_prog)
@@ -1948,6 +2259,10 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
if (!bpf_allow_tail_call_in_subprogs(env))
prog->aux->stack_depth = MAX_BPF_STACK;
prog->aux->max_pkt_offset = MAX_PACKET_OFF;
+ prog->aux->max_rdonly_access = U32_MAX;
+ prog->aux->max_rdwr_access = U32_MAX;
+ if (prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE)
+ prog->aux->max_tp_access = U32_MAX;
/* mark bpf_tail_call as different opcode to avoid
* conditional branch in the interpreter for every normal
@@ -2013,7 +2328,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
goto next_insn;
}
- if (insn->imm == BPF_FUNC_timer_set_callback) {
+ aux = &env->insn_aux_data[i + delta];
+ if (aux->arg_prog) {
/* The verifier will process callback_fn as many times as necessary
* with different maps and the register states prepared by
* set_timer_callback_state will be accurate.
@@ -2028,7 +2344,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
* bpf_timer_set_callback-ed will return -EINVAL.
*/
struct bpf_insn ld_addrs[2] = {
- BPF_LD_IMM64(BPF_REG_3, (long)prog->aux),
+ BPF_LD_IMM64(aux->arg_prog, (long)prog->aux),
};
insn_buf[0] = ld_addrs[0];
@@ -2256,7 +2572,7 @@ patch_map_ops_generic:
cnt = 2;
}
insn_buf[cnt++] = BPF_JMP32_REG(BPF_JGE, BPF_REG_2, BPF_REG_0, 6);
- insn_buf[cnt++] = BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 3);
+ insn_buf[cnt++] = BPF_ALU32_IMM(BPF_LSH, BPF_REG_2, 3);
insn_buf[cnt++] = BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_1);
insn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 0);
insn_buf[cnt++] = BPF_STX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0);
@@ -2477,9 +2793,17 @@ next_insn:
BPF_MAX_LOOPS);
}
/* Copy first actual insn to preserve it */
- insn_buf[cnt++] = env->prog->insnsi[subprog_start];
+ insn_buf[cnt] = env->prog->insnsi[subprog_start];
+ ret = bpf_adj_moved_insn(&insn_buf[cnt], cnt);
+ if (ret) {
+ verbose(env, "insn %d cannot be patched due to 16-bit range\n",
+ env->insn_aux_data[subprog_start].orig_idx);
+ return ret;
+ }
+ cnt++;
- new_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt);
+ new_prog = __bpf_patch_insn_data(env, subprog_start, insn_buf, cnt,
+ BPF_PATCH_MOVE_TARGET);
if (!new_prog)
return -ENOMEM;
env->prog = prog = new_prog;
@@ -2509,10 +2833,6 @@ next_insn:
}
}
- ret = sort_kfunc_descs_by_imm_off(env);
- if (ret)
- return ret;
-
return 0;
}
@@ -2686,4 +3006,3 @@ int bpf_remove_fastcall_spills_fills(struct bpf_verifier_env *env)
return 0;
}
-
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 13a2356c84cf..bf78e09f4d51 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -100,8 +100,10 @@ struct bpf_htab {
struct percpu_counter pcount;
atomic_t count;
bool use_percpu_counter;
+ bool has_hash;
u32 n_buckets; /* number of hash buckets */
u32 elem_size; /* size of each element in bytes */
+ u32 elem_offset;/* offset of htab_elem in bytes */
u32 hashrnd;
};
@@ -117,19 +119,46 @@ struct htab_elem {
};
};
};
- union {
- /* pointer to per-cpu pointer */
- void *ptr_to_pptr;
- struct bpf_lru_node lru_node;
- };
- u32 hash;
char key[] __aligned(8);
};
+struct htab_elem_hashed {
+ u32 hash __aligned(8);
+ struct htab_elem elem;
+};
+
+struct htab_elem_lru {
+ struct bpf_lru_node lru_node;
+ struct htab_elem_hashed;
+};
+
+/*
+ * Only for non-preallocated PCPU maps with small keys. Preallocated PCPU maps
+ * don't need ptr_to_pptr, and use htab_elem.
+ */
+struct htab_elem_pcpu {
+ /* pointer to per-cpu pointer */
+ void *ptr_to_pptr;
+ struct htab_elem elem;
+};
+
+/*
+ * Only for non-preallocated PCPU maps with large keys. Preallocated PCPU maps
+ * don't need ptr_to_pptr, and use htab_elem_hashed.
+ */
+struct htab_elem_pcpu_hashed {
+ /* pointer to per-cpu pointer */
+ void *ptr_to_pptr;
+ struct htab_elem_hashed;
+};
+
+static_assert(offsetof(struct htab_elem_pcpu_hashed, ptr_to_pptr) ==
+ offsetof(struct htab_elem_pcpu, ptr_to_pptr));
+
struct htab_btf_record {
struct btf_record *record;
struct btf *btf;
- u32 key_size;
+ u32 value_offset;
};
static inline bool htab_is_prealloc(const struct bpf_htab *htab)
@@ -184,6 +213,71 @@ static inline bool is_fd_htab(const struct bpf_htab *htab)
return htab->map.map_type == BPF_MAP_TYPE_HASH_OF_MAPS;
}
+/*
+ * u32 hash reads are always atomic. If the hash is elided, key comparisons
+ * must also be atomic to avoid false positive matches from torn key
+ * reads/writes on recycled elements. That is only possible when the
+ * key fits within a word.
+ */
+static __always_inline bool htab_key_size_needs_hash(u32 key_size)
+{
+ return key_size > sizeof(unsigned long);
+}
+
+/*
+ * When hash is omitted, key reads and writes must be atomic to support lockless
+ * RCU readers. Zero-extend keys to the word size to support all sub-word key
+ * sizes, regardless of the size of the caller's key allocation.
+ */
+static __always_inline unsigned long htab_zero_extend_key(const void *key,
+ u32 key_size)
+{
+ unsigned long k = 0;
+
+ memcpy(&k, key, min_t(u32, key_size, sizeof(k)));
+ return k;
+}
+
+static bool htab_has_hash(const struct bpf_htab *htab)
+{
+ return htab->has_hash;
+}
+
+static u32 htab_elem_hash(struct htab_elem *l)
+{
+ return container_of(l, struct htab_elem_hashed, elem)->hash;
+}
+
+static void htab_elem_set_hash(struct htab_elem *l, u32 hash)
+{
+ container_of(l, struct htab_elem_hashed, elem)->hash = hash;
+}
+
+static void *htab_elem_container(const struct bpf_htab *htab, struct htab_elem *l)
+{
+ return (void *)l - htab->elem_offset;
+}
+
+static void *htab_elem_get_ptr_to_pptr(const struct bpf_htab *htab, struct htab_elem *l)
+{
+ struct htab_elem_pcpu *pcpu_elem = htab_elem_container(htab, l);
+
+ return pcpu_elem->ptr_to_pptr;
+}
+
+static void htab_elem_set_ptr_to_pptr(const struct bpf_htab *htab, struct htab_elem *l,
+ void *ptr)
+{
+ struct htab_elem_pcpu *pcpu_elem = htab_elem_container(htab, l);
+
+ pcpu_elem->ptr_to_pptr = ptr;
+}
+
+static struct bpf_lru_node *htab_elem_lru_node(struct htab_elem *l)
+{
+ return &container_of(l, struct htab_elem_lru, elem)->lru_node;
+}
+
static inline void *htab_elem_value(struct htab_elem *l, u32 key_size)
{
return l->key + round_up(key_size, 8);
@@ -207,7 +301,7 @@ static void *fd_htab_map_get_ptr(const struct bpf_map *map, struct htab_elem *l)
static struct htab_elem *get_htab_elem(struct bpf_htab *htab, int i)
{
- return (struct htab_elem *) (htab->elems + i * (u64)htab->elem_size);
+ return htab->elems + i * (u64)htab->elem_size + htab->elem_offset;
}
/* Both percpu and fd htab support in-place update, so no need for
@@ -301,16 +395,16 @@ free_elems:
* bucket_lock followed by lru_lock is not allowed. In such cases,
* bucket_lock needs to be released first before acquiring lru_lock.
*/
-static struct htab_elem *prealloc_lru_pop(struct bpf_htab *htab, void *key,
- u32 hash)
+static struct htab_elem_lru *prealloc_lru_pop(struct bpf_htab *htab, void *key,
+ u32 hash)
{
struct bpf_lru_node *node = bpf_lru_pop_free(&htab->lru, hash);
- struct htab_elem *l;
+ struct htab_elem_lru *l;
if (node) {
bpf_map_inc_elem_count(&htab->map);
- l = container_of(node, struct htab_elem, lru_node);
- memcpy(l->key, key, htab->map.key_size);
+ l = container_of(node, struct htab_elem_lru, lru_node);
+ memcpy(l->elem.key, key, htab->map.key_size);
return l;
}
@@ -350,8 +444,8 @@ skip_percpu_elems:
if (htab_is_lru(htab))
err = bpf_lru_init(&htab->lru,
htab->map.map_flags & BPF_F_NO_COMMON_LRU,
- offsetof(struct htab_elem, hash) -
- offsetof(struct htab_elem, lru_node),
+ offsetof(struct htab_elem_lru, hash) -
+ offsetof(struct htab_elem_lru, lru_node),
htab_lru_map_delete_node,
htab);
else
@@ -362,11 +456,12 @@ skip_percpu_elems:
if (htab_is_lru(htab))
bpf_lru_populate(&htab->lru, htab->elems,
- offsetof(struct htab_elem, lru_node),
+ offsetof(struct htab_elem_lru, lru_node),
htab->elem_size, num_entries);
else
pcpu_freelist_populate(&htab->freelist,
- htab->elems + offsetof(struct htab_elem, fnode),
+ htab->elems + htab->elem_offset +
+ offsetof(struct htab_elem, fnode),
htab->elem_size, num_entries);
return 0;
@@ -454,7 +549,7 @@ static int htab_map_alloc_check(union bpf_attr *attr)
return -EINVAL;
if ((u64)attr->key_size + attr->value_size >= KMALLOC_MAX_SIZE -
- sizeof(struct htab_elem))
+ sizeof(struct htab_elem_lru))
/* if key_size + value_size is bigger, the user space won't be
* able to access the elements via bpf syscall. This check
* also makes sure that the elem_size doesn't overflow and it's
@@ -471,14 +566,11 @@ static int htab_map_alloc_check(union bpf_attr *attr)
static void htab_mem_dtor(void *obj, void *ctx)
{
struct htab_btf_record *hrec = ctx;
- struct htab_elem *elem = obj;
- void *map_value;
if (IS_ERR_OR_NULL(hrec->record))
return;
- map_value = htab_elem_value(elem, hrec->key_size);
- bpf_obj_free_fields(hrec->record, map_value);
+ bpf_obj_free_fields(hrec->record, obj + hrec->value_offset);
}
static void htab_pcpu_mem_dtor(void *obj, void *ctx)
@@ -508,7 +600,7 @@ static void htab_dtor_ctx_free(void *ctx)
}
static int bpf_ma_set_dtor(struct bpf_map *map, struct bpf_mem_alloc *ma,
- void (*dtor)(void *, void *))
+ void (*dtor)(void *, void *), u32 value_offset)
{
struct htab_btf_record *hrec;
int err;
@@ -520,7 +612,7 @@ static int bpf_ma_set_dtor(struct bpf_map *map, struct bpf_mem_alloc *ma,
hrec = kzalloc_obj(*hrec);
if (!hrec)
return -ENOMEM;
- hrec->key_size = map->key_size;
+ hrec->value_offset = value_offset;
hrec->record = btf_record_dup(map->record);
if (IS_ERR(hrec->record)) {
err = PTR_ERR(hrec->record);
@@ -555,9 +647,12 @@ static int htab_map_check_btf(struct bpf_map *map, const struct btf *btf,
* populated in htab_map_alloc(), so it will always appear as NULL.
*/
if (htab_is_percpu(htab))
- return bpf_ma_set_dtor(map, &htab->pcpu_ma, htab_pcpu_mem_dtor);
+ return bpf_ma_set_dtor(map, &htab->pcpu_ma, htab_pcpu_mem_dtor, 0);
else
- return bpf_ma_set_dtor(map, &htab->ma, htab_mem_dtor);
+ return bpf_ma_set_dtor(map, &htab->ma, htab_mem_dtor,
+ htab->elem_offset +
+ offsetof(struct htab_elem, key) +
+ round_up(map->key_size, 8));
}
static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
@@ -580,6 +675,10 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
bpf_map_init_from_attr(&htab->map, attr);
+ /* Avoid hash memory use and comparisons where unnecessary. */
+ htab->has_hash = htab_is_lru(htab) ||
+ htab_key_size_needs_hash(htab->map.key_size);
+
if (percpu_lru) {
/* ensure each CPU's lru list has >=1 elements.
* since we are at it, make each lru list has the same
@@ -601,7 +700,17 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
htab->n_buckets = roundup_pow_of_two(htab->map.max_entries);
- htab->elem_size = sizeof(struct htab_elem) +
+ if (htab_is_lru(htab))
+ htab->elem_offset = offsetof(struct htab_elem_lru, elem);
+ else if (percpu && !prealloc)
+ htab->elem_offset = htab_has_hash(htab) ?
+ offsetof(struct htab_elem_pcpu_hashed, elem) :
+ offsetof(struct htab_elem_pcpu, elem);
+ else if (htab_has_hash(htab))
+ htab->elem_offset = offsetof(struct htab_elem_hashed, elem);
+
+ htab->elem_size = htab->elem_offset +
+ sizeof(struct htab_elem) +
round_up(htab->map.key_size, 8);
if (percpu)
htab->elem_size += sizeof(void *);
@@ -692,11 +801,18 @@ free_htab:
return ERR_PTR(err);
}
-static inline u32 htab_map_hash(const void *key, u32 key_len, u32 hashrnd)
+static __always_inline u32 htab_map_hash(const void *key, u32 key_len, u32 hashrnd)
{
- if (likely(key_len % 4 == 0))
+ const u32 *k = key;
+
+ if (key_len == sizeof(u32))
+ return jhash_1word(k[0], hashrnd);
+ else if (key_len == sizeof(u64))
+ return jhash_2words(k[0], k[1], hashrnd);
+ else if (likely(key_len % 4 == 0))
return jhash2(key, key_len / 4, hashrnd);
- return jhash(key, key_len, hashrnd);
+ else
+ return jhash(key, key_len, hashrnd);
}
static inline struct bucket *__select_bucket(struct bpf_htab *htab, u32 hash)
@@ -709,35 +825,57 @@ static inline struct hlist_nulls_head *select_bucket(struct bpf_htab *htab, u32
return &__select_bucket(htab, hash)->head;
}
-/* this lookup function can only be called with bucket lock taken */
-static struct htab_elem *lookup_elem_raw(struct hlist_nulls_head *head, u32 hash,
- void *key, u32 key_size)
+static __always_inline struct hlist_nulls_node *
+__lookup_elem_raw(struct hlist_nulls_head *head, u32 hash, void *key,
+ u32 key_size, bool elem_has_hash)
{
struct hlist_nulls_node *n;
struct htab_elem *l;
- hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
- if (l->hash == hash && !memcmp(&l->key, key, key_size))
- return l;
+ if (elem_has_hash) {
+ hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
+ if (htab_elem_hash(l) == hash &&
+ !memcmp(&l->key, key, key_size))
+ break;
+ } else {
+ unsigned long k = htab_zero_extend_key(key, key_size);
- return NULL;
+ hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
+ if (READ_ONCE(*(unsigned long *)l->key) == k)
+ break;
+ }
+
+ return n;
+}
+
+/* this lookup function can only be called with bucket lock taken */
+static __always_inline struct htab_elem *
+lookup_elem_raw(struct hlist_nulls_head *head, u32 hash, void *key,
+ u32 key_size, bool elem_has_hash)
+{
+ struct hlist_nulls_node *n;
+
+ n = __lookup_elem_raw(head, hash, key, key_size, elem_has_hash);
+ if (is_a_nulls(n))
+ return NULL;
+
+ return hlist_nulls_entry(n, struct htab_elem, hash_node);
}
/* can be called without bucket lock. it will repeat the loop in
* the unlikely event when elements moved from one bucket into another
* while link list is being walked
*/
-static struct htab_elem *lookup_nulls_elem_raw(struct hlist_nulls_head *head,
- u32 hash, void *key,
- u32 key_size, u32 n_buckets)
+static __always_inline struct htab_elem *
+lookup_nulls_elem_raw(struct hlist_nulls_head *head, u32 hash, void *key,
+ u32 key_size, bool elem_has_hash, u32 n_buckets)
{
struct hlist_nulls_node *n;
- struct htab_elem *l;
again:
- hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
- if (l->hash == hash && !memcmp(&l->key, key, key_size))
- return l;
+ n = __lookup_elem_raw(head, hash, key, key_size, elem_has_hash);
+ if (!is_a_nulls(n))
+ return hlist_nulls_entry(n, struct htab_elem, hash_node);
if (unlikely(get_nulls_value(n) != (hash & (n_buckets - 1))))
goto again;
@@ -745,31 +883,56 @@ again:
return NULL;
}
-/* Called from syscall or from eBPF program directly, so
- * arguments have to match bpf_map_lookup_elem() exactly.
- * The return value is adjusted by BPF instructions
- * in htab_map_gen_lookup().
- */
-static void *__htab_map_lookup_elem(struct bpf_map *map, void *key)
+static __always_inline void *__htab_lookup(struct bpf_map *map, void *key,
+ u32 key_size, bool elem_has_hash)
{
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
struct hlist_nulls_head *head;
struct htab_elem *l;
- u32 hash, key_size;
+ u32 hash;
WARN_ON_ONCE(!bpf_rcu_lock_held());
- key_size = map->key_size;
-
hash = htab_map_hash(key, key_size, htab->hashrnd);
head = select_bucket(htab, hash);
- l = lookup_nulls_elem_raw(head, hash, key, key_size, htab->n_buckets);
+ l = lookup_nulls_elem_raw(head, hash, key, key_size, elem_has_hash,
+ htab->n_buckets);
return l;
}
+/*
+ * Called from syscall or from eBPF program directly, so
+ * arguments have to match bpf_map_lookup_elem() exactly.
+ * The return value is adjusted by BPF instructions
+ * in htab_map_gen_lookup(). u32/u64 specializations
+ * allow utilizing compile-time key_size for optimization.
+ */
+static void *__htab_map_lookup_elem(struct bpf_map *map, void *key)
+{
+ struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+
+ return __htab_lookup(map, key, map->key_size, htab_has_hash(htab));
+}
+
+/*
+ * Specialized lookups for htab_map_gen_lookup(). These are only used for
+ * BPF_MAP_TYPE_HASH (never LRU), so elem_has_hash depends only on key size.
+ */
+static void *__htab_map_lookup_elem_u32(struct bpf_map *map, void *key)
+{
+ return __htab_lookup(map, key, sizeof(u32),
+ htab_key_size_needs_hash(sizeof(u32)));
+}
+
+static void *__htab_map_lookup_elem_u64(struct bpf_map *map, void *key)
+{
+ return __htab_lookup(map, key, sizeof(u64),
+ htab_key_size_needs_hash(sizeof(u64)));
+}
+
static void *htab_map_lookup_elem(struct bpf_map *map, void *key)
{
struct htab_elem *l = __htab_map_lookup_elem(map, key);
@@ -798,7 +961,16 @@ static int htab_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
(void *(*)(struct bpf_map *map, void *key))NULL));
- *insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem);
+ BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem_u32,
+ (void *(*)(struct bpf_map *map, void *key))NULL));
+ BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem_u64,
+ (void *(*)(struct bpf_map *map, void *key))NULL));
+ if (map->key_size == sizeof(u32))
+ *insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem_u32);
+ else if (map->key_size == sizeof(u64))
+ *insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem_u64);
+ else
+ *insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem);
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 1);
*insn++ = BPF_ALU64_IMM(BPF_ADD, ret,
offsetof(struct htab_elem, key) +
@@ -813,7 +985,7 @@ static __always_inline void *__htab_lru_map_lookup_elem(struct bpf_map *map,
if (l) {
if (mark)
- bpf_lru_node_set_ref(&l->lru_node);
+ bpf_lru_node_set_ref(htab_elem_lru_node(l));
return htab_elem_value(l, map->key_size);
}
@@ -836,19 +1008,17 @@ static int htab_lru_map_gen_lookup(struct bpf_map *map,
struct bpf_insn *insn = insn_buf;
const int ret = BPF_REG_0;
const int ref_reg = BPF_REG_1;
+ const s16 ref_off = (int)offsetof(struct htab_elem_lru, lru_node) +
+ (int)offsetof(struct bpf_lru_node, ref) -
+ (int)offsetof(struct htab_elem_lru, elem);
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
(void *(*)(struct bpf_map *map, void *key))NULL));
*insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem);
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 4);
- *insn++ = BPF_LDX_MEM(BPF_B, ref_reg, ret,
- offsetof(struct htab_elem, lru_node) +
- offsetof(struct bpf_lru_node, ref));
+ *insn++ = BPF_LDX_MEM(BPF_B, ref_reg, ret, ref_off);
*insn++ = BPF_JMP_IMM(BPF_JNE, ref_reg, 0, 1);
- *insn++ = BPF_ST_MEM(BPF_B, ret,
- offsetof(struct htab_elem, lru_node) +
- offsetof(struct bpf_lru_node, ref),
- 1);
+ *insn++ = BPF_ST_MEM(BPF_B, ret, ref_off, 1);
*insn++ = BPF_ALU64_IMM(BPF_ADD, ret,
offsetof(struct htab_elem, key) +
round_up(map->key_size, 8));
@@ -880,14 +1050,15 @@ static void check_and_cancel_fields(struct bpf_htab *htab,
static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node)
{
struct bpf_htab *htab = arg;
- struct htab_elem *l = NULL, *tgt_l;
+ struct htab_elem_lru *tgt_l;
+ struct htab_elem *l = NULL;
struct hlist_nulls_head *head;
struct hlist_nulls_node *n;
unsigned long flags;
struct bucket *b;
int ret;
- tgt_l = container_of(node, struct htab_elem, lru_node);
+ tgt_l = container_of(node, struct htab_elem_lru, lru_node);
b = __select_bucket(htab, tgt_l->hash);
head = &b->head;
@@ -896,7 +1067,7 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node)
return false;
hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
- if (l == tgt_l) {
+ if (l == &tgt_l->elem) {
hlist_nulls_del_rcu(&l->hash_node);
bpf_map_dec_elem_count(&htab->map);
break;
@@ -904,9 +1075,9 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node)
htab_unlock_bucket(b, flags);
- if (l == tgt_l)
+ if (l == &tgt_l->elem)
check_and_cancel_fields(htab, l);
- return l == tgt_l;
+ return l == &tgt_l->elem;
}
/* Called from syscall */
@@ -930,7 +1101,8 @@ static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
head = select_bucket(htab, hash);
/* lookup the key */
- l = lookup_nulls_elem_raw(head, hash, key, key_size, htab->n_buckets);
+ l = lookup_nulls_elem_raw(head, hash, key, key_size,
+ htab_has_hash(htab), htab->n_buckets);
if (!l)
goto find_first_elem;
@@ -973,8 +1145,8 @@ static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l)
check_and_cancel_fields(htab, l);
if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH)
- bpf_mem_cache_free(&htab->pcpu_ma, l->ptr_to_pptr);
- bpf_mem_cache_free(&htab->ma, l);
+ bpf_mem_cache_free(&htab->pcpu_ma, htab_elem_get_ptr_to_pptr(htab, l));
+ bpf_mem_cache_free(&htab->ma, htab_elem_container(htab, l));
}
static void htab_put_fd_value(struct bpf_htab *htab, struct htab_elem *l)
@@ -1107,11 +1279,17 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
if (prealloc) {
if (old_elem) {
/* if we're updating the existing element,
- * use per-cpu extra elems to avoid freelist_pop/push
+ * use per-cpu extra elems to avoid freelist_pop/push.
+ * The caller puts old_elem into the slot once it is
+ * unlinked. Until then the slot is empty for an NMI
+ * or a kprobe that updates this map on this cpu.
*/
pl_new = this_cpu_ptr(htab->extra_elems);
- l_new = *pl_new;
- *pl_new = old_elem;
+ l_new = READ_ONCE(*pl_new);
+ do {
+ if (!l_new)
+ return ERR_PTR(-EBUSY);
+ } while (!try_cmpxchg_local(pl_new, &l_new, NULL));
} else {
struct pcpu_freelist_node *l;
@@ -1122,6 +1300,8 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
bpf_map_inc_elem_count(&htab->map);
}
} else {
+ void *container;
+
if (is_map_full(htab))
if (!old_elem)
/* when map is full and update() is replacing
@@ -1131,14 +1311,17 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
*/
return ERR_PTR(-E2BIG);
inc_elem_count(htab);
- l_new = bpf_mem_cache_alloc(&htab->ma);
- if (!l_new) {
+ container = bpf_mem_cache_alloc(&htab->ma);
+ if (!container) {
l_new = ERR_PTR(-ENOMEM);
goto dec_count;
}
+ l_new = container + htab->elem_offset;
}
- memcpy(l_new->key, key, key_size);
+ if (htab_has_hash(htab))
+ memcpy(l_new->key, key, key_size);
+
if (percpu) {
if (prealloc) {
pptr = htab_elem_get_ptr(l_new, key_size);
@@ -1147,11 +1330,11 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
void *ptr = bpf_mem_cache_alloc(&htab->pcpu_ma);
if (!ptr) {
- bpf_mem_cache_free(&htab->ma, l_new);
+ bpf_mem_cache_free(&htab->ma, htab_elem_container(htab, l_new));
l_new = ERR_PTR(-ENOMEM);
goto dec_count;
}
- l_new->ptr_to_pptr = ptr;
+ htab_elem_set_ptr_to_pptr(htab, l_new, ptr);
pptr = *(void __percpu **)ptr;
}
@@ -1170,7 +1353,11 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
copy_map_value(&htab->map, htab_elem_value(l_new, key_size), value);
}
- l_new->hash = hash;
+ if (htab_has_hash(htab))
+ htab_elem_set_hash(l_new, hash);
+ else
+ WRITE_ONCE(*(unsigned long *)l_new->key,
+ htab_zero_extend_key(key, key_size));
return l_new;
dec_count:
dec_elem_count(htab);
@@ -1180,11 +1367,11 @@ dec_count:
static int check_flags(struct bpf_htab *htab, struct htab_elem *l_old,
u64 map_flags)
{
- if (l_old && (map_flags & ~BPF_F_LOCK) == BPF_NOEXIST)
+ if (l_old && (map_flags & BPF_NOEXIST))
/* elem already exists */
return -EEXIST;
- if (!l_old && (map_flags & ~BPF_F_LOCK) == BPF_EXIST)
+ if (!l_old && (map_flags & BPF_EXIST))
/* elem doesn't exist, cannot update it */
return -ENOENT;
@@ -1221,6 +1408,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value,
return -EINVAL;
/* find an element without taking the bucket lock */
l_old = lookup_nulls_elem_raw(head, hash, key, key_size,
+ htab_has_hash(htab),
htab->n_buckets);
ret = check_flags(htab, l_old, map_flags);
if (ret)
@@ -1242,7 +1430,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value,
if (ret)
return ret;
- l_old = lookup_elem_raw(head, hash, key, key_size);
+ l_old = lookup_elem_raw(head, hash, key, key_size, htab_has_hash(htab));
ret = check_flags(htab, l_old, map_flags);
if (ret)
@@ -1277,11 +1465,15 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value,
if (l_old) {
hlist_nulls_del_rcu(&l_old->hash_node);
- /* l_old has already been stashed in htab->extra_elems, cancel
- * its reusable special fields before it is available for reuse.
+ /*
+ * l_old replaces the extra element that alloc_htab_elem() took.
+ * Cancel its reusable special fields first: once it is in the
+ * slot, the next update on this cpu can reuse it.
*/
- if (htab_is_prealloc(htab))
+ if (htab_is_prealloc(htab)) {
check_and_cancel_fields(htab, l_old);
+ WRITE_ONCE(*this_cpu_ptr(htab->extra_elems), l_old);
+ }
}
htab_unlock_bucket(b, flags);
if (l_old && !htab_is_prealloc(htab))
@@ -1296,14 +1488,15 @@ static void htab_lru_push_free(struct bpf_htab *htab, struct htab_elem *elem)
{
check_and_cancel_fields(htab, elem);
bpf_map_dec_elem_count(&htab->map);
- bpf_lru_push_free(&htab->lru, &elem->lru_node);
+ bpf_lru_push_free(&htab->lru, htab_elem_lru_node(elem));
}
static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value,
u64 map_flags)
{
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
- struct htab_elem *l_new, *l_old = NULL;
+ struct htab_elem *l_old = NULL;
+ struct htab_elem_lru *l_new;
struct hlist_nulls_head *head;
unsigned long flags;
struct bucket *b;
@@ -1331,13 +1524,13 @@ static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value
l_new = prealloc_lru_pop(htab, key, hash);
if (!l_new)
return -ENOMEM;
- copy_map_value(&htab->map, htab_elem_value(l_new, map->key_size), value);
+ copy_map_value(&htab->map, htab_elem_value(&l_new->elem, map->key_size), value);
ret = htab_lock_bucket(b, &flags);
if (ret)
goto err_lock_bucket;
- l_old = lookup_elem_raw(head, hash, key, key_size);
+ l_old = lookup_elem_raw(head, hash, key, key_size, true);
ret = check_flags(htab, l_old, map_flags);
if (ret)
@@ -1346,7 +1539,7 @@ static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value
/* add new element to the head of the list, so that
* concurrent search will find it before old elem
*/
- hlist_nulls_add_head_rcu(&l_new->hash_node, head);
+ hlist_nulls_add_head_rcu(&l_new->elem.hash_node, head);
if (l_old) {
bpf_lru_node_set_ref(&l_new->lru_node);
hlist_nulls_del_rcu(&l_old->hash_node);
@@ -1358,7 +1551,7 @@ err:
err_lock_bucket:
if (ret)
- htab_lru_push_free(htab, l_new);
+ htab_lru_push_free(htab, &l_new->elem);
else if (l_old)
htab_lru_push_free(htab, l_old);
@@ -1367,9 +1560,11 @@ err_lock_bucket:
static int htab_map_check_update_flags(bool onallcpus, u64 map_flags)
{
+ if (unlikely((map_flags & BPF_EXIST_FLAGS) == BPF_EXIST_FLAGS))
+ return -EINVAL;
if (unlikely(!onallcpus && map_flags > BPF_EXIST))
return -EINVAL;
- if (unlikely(onallcpus && ((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS)))
+ if (unlikely(onallcpus && ((u32)map_flags & ~(BPF_EXIST_FLAGS | BPF_CPU_FLAGS))))
return -EINVAL;
return 0;
}
@@ -1404,7 +1599,7 @@ static long htab_map_update_elem_in_place(struct bpf_map *map, void *key,
if (ret)
return ret;
- l_old = lookup_elem_raw(head, hash, key, key_size);
+ l_old = lookup_elem_raw(head, hash, key, key_size, htab_has_hash(htab));
ret = check_flags(htab, l_old, map_flags);
if (ret)
@@ -1442,7 +1637,8 @@ static long __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
bool onallcpus)
{
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
- struct htab_elem *l_new = NULL, *l_old;
+ struct htab_elem_lru *l_new = NULL;
+ struct htab_elem *l_old;
struct hlist_nulls_head *head;
unsigned long flags;
struct bucket *b;
@@ -1467,7 +1663,7 @@ static long __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
* to remove older elem from htab and this removal
* operation will need a bucket lock.
*/
- if (map_flags != BPF_EXIST) {
+ if (!(map_flags & BPF_EXIST)) {
l_new = prealloc_lru_pop(htab, key, hash);
if (!l_new)
return -ENOMEM;
@@ -1477,22 +1673,22 @@ static long __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
if (ret)
goto err_lock_bucket;
- l_old = lookup_elem_raw(head, hash, key, key_size);
+ l_old = lookup_elem_raw(head, hash, key, key_size, true);
ret = check_flags(htab, l_old, map_flags);
if (ret)
goto err;
if (l_old) {
- bpf_lru_node_set_ref(&l_old->lru_node);
+ bpf_lru_node_set_ref(htab_elem_lru_node(l_old));
/* per-cpu hash map can update value in-place */
pcpu_copy_value(htab, htab_elem_get_ptr(l_old, key_size),
value, onallcpus, map_flags);
} else {
- pcpu_init_value(htab, htab_elem_get_ptr(l_new, key_size),
+ pcpu_init_value(htab, htab_elem_get_ptr(&l_new->elem, key_size),
value, onallcpus, map_flags);
- hlist_nulls_add_head_rcu(&l_new->hash_node, head);
+ hlist_nulls_add_head_rcu(&l_new->elem.hash_node, head);
l_new = NULL;
}
ret = 0;
@@ -1542,7 +1738,7 @@ static long htab_map_delete_elem(struct bpf_map *map, void *key)
if (ret)
return ret;
- l = lookup_elem_raw(head, hash, key, key_size);
+ l = lookup_elem_raw(head, hash, key, key_size, htab_has_hash(htab));
if (l)
hlist_nulls_del_rcu(&l->hash_node);
else
@@ -1577,7 +1773,7 @@ static long htab_lru_map_delete_elem(struct bpf_map *map, void *key)
if (ret)
return ret;
- l = lookup_elem_raw(head, hash, key, key_size);
+ l = lookup_elem_raw(head, hash, key, key_size, true);
if (l)
hlist_nulls_del_rcu(&l->hash_node);
@@ -1718,7 +1914,7 @@ static int __htab_map_lookup_and_delete_elem(struct bpf_map *map, void *key,
if (ret)
return ret;
- l = lookup_elem_raw(head, hash, key, key_size);
+ l = lookup_elem_raw(head, hash, key, key_size, htab_has_hash(htab));
if (!l) {
ret = -ENOENT;
goto out_unlock;
@@ -2489,7 +2685,7 @@ static void *htab_lru_percpu_map_lookup_elem(struct bpf_map *map, void *key)
struct htab_elem *l = __htab_map_lookup_elem(map, key);
if (l) {
- bpf_lru_node_set_ref(&l->lru_node);
+ bpf_lru_node_set_ref(htab_elem_lru_node(l));
return this_cpu_ptr(htab_elem_get_ptr(l, map->key_size));
}
@@ -2505,7 +2701,7 @@ static void *htab_lru_percpu_map_lookup_percpu_elem(struct bpf_map *map, void *k
l = __htab_map_lookup_elem(map, key);
if (l) {
- bpf_lru_node_set_ref(&l->lru_node);
+ bpf_lru_node_set_ref(htab_elem_lru_node(l));
return per_cpu_ptr(htab_elem_get_ptr(l, map->key_size), cpu);
}
@@ -2904,18 +3100,6 @@ static int rhtab_map_alloc_check(union bpf_attr *attr)
return htab_map_alloc_check(attr);
}
-static void rhtab_mem_dtor(void *obj, void *ctx)
-{
- struct htab_btf_record *hrec = ctx;
- struct rhtab_elem *elem = obj;
-
- if (IS_ERR_OR_NULL(hrec->record))
- return;
-
- bpf_obj_free_fields(hrec->record,
- rhtab_elem_value(elem, hrec->key_size));
-}
-
static void rhtab_free_elem(void *ptr, void *arg)
{
struct bpf_rhtab *rhtab = arg;
@@ -3141,7 +3325,9 @@ static int rhtab_map_check_btf(struct bpf_map *map, const struct btf *btf,
if (btf_type_is_void(key_type))
return -EINVAL;
- return bpf_ma_set_dtor(map, &rhtab->ma, rhtab_mem_dtor);
+ return bpf_ma_set_dtor(map, &rhtab->ma, htab_mem_dtor,
+ offsetof(struct rhtab_elem, data) +
+ round_up(map->key_size, 8));
}
static void rhtab_map_free_internal_structs(struct bpf_map *map)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 712dca5a2c5b..648abe1ba63a 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -679,9 +679,44 @@ const struct bpf_func_proto bpf_copy_from_user_proto = {
.arg3_type = ARG_ANYTHING,
};
+static int __bpf_copy_from_user_mm(void *dst, u32 size,
+ const void __user *user_ptr,
+ struct mm_struct *mm)
+{
+ int ret;
+
+ ret = access_remote_vm(mm, (unsigned long)user_ptr, dst, size, 0);
+ if (ret == size)
+ return 0;
+
+ memset(dst, 0, size);
+ /* Return -EFAULT for partial read */
+ return ret < 0 ? ret : -EFAULT;
+}
+
+static int __bpf_copy_from_user_mm_str(void *dst, u32 size,
+ const void __user *user_ptr,
+ struct mm_struct *mm, u64 flags)
+{
+ int ret;
+
+ ret = copy_remote_mm_str(mm, (unsigned long)user_ptr, dst, size, 0);
+ if (ret < 0) {
+ if (flags & BPF_F_PAD_ZEROS)
+ memset(dst, 0, size);
+ return ret;
+ }
+
+ if (flags & BPF_F_PAD_ZEROS)
+ memset(dst + ret, 0, size - ret);
+
+ return ret + 1;
+}
+
BPF_CALL_5(bpf_copy_from_user_task, void *, dst, u32, size,
const void __user *, user_ptr, struct task_struct *, tsk, u64, flags)
{
+ struct mm_struct *mm;
int ret;
/* flags is not used yet */
@@ -691,13 +726,16 @@ BPF_CALL_5(bpf_copy_from_user_task, void *, dst, u32, size,
if (unlikely(!size))
return 0;
- ret = access_process_vm(tsk, (unsigned long)user_ptr, dst, size, 0);
- if (ret == size)
- return 0;
+ mm = get_task_mm(tsk);
+ if (!mm) {
+ memset(dst, 0, size);
+ return -EFAULT;
+ }
- memset(dst, 0, size);
- /* Return -EFAULT for partial read */
- return ret < 0 ? ret : -EFAULT;
+ ret = __bpf_copy_from_user_mm(dst, size, user_ptr, mm);
+ mmput(mm);
+
+ return ret;
}
const struct bpf_func_proto bpf_copy_from_user_task_proto = {
@@ -1092,7 +1130,7 @@ const struct bpf_func_proto bpf_snprintf_proto = {
.func = bpf_snprintf,
.gpl_only = true,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_PTR_TO_MEM_OR_NULL | MEM_WRITE,
+ .arg1_type = ARG_PTR_TO_MEM_OR_NULL | MEM_UNINIT | MEM_WRITE,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_PTR_TO_CONST_STR,
.arg4_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
@@ -1224,18 +1262,21 @@ static void bpf_wq_work(struct work_struct *work)
BTF_TYPE_EMIT(struct bpf_wq);
+ /* keeps the program that callback_fn belongs to */
+ rcu_read_lock_trace();
+
callback_fn = READ_ONCE(cb->callback_fn);
if (!callback_fn)
- return;
+ goto out;
key = map_key_from_value(map, value, &idx);
- rcu_read_lock_trace();
- migrate_disable();
+ migrate_disable();
callback_fn((u64)(long)map, (u64)(long)key, (u64)(long)value, 0, 0);
migrate_enable();
+out:
rcu_read_unlock_trace();
}
@@ -1510,6 +1551,7 @@ static const struct bpf_func_proto bpf_timer_set_callback_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_TIMER,
.arg2_type = ARG_PTR_TO_FUNC,
+ .arg3_type = ARG_PTR_TO_PROG_AUX,
};
static bool defer_timer_wq_op(void)
@@ -3659,6 +3701,92 @@ __bpf_kfunc int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void __user
}
/**
+ * bpf_copy_from_user_mm() - Copy data from an address space
+ * @dst: Destination address, in kernel space
+ * @dst__sz: Number of bytes to copy
+ * @unsafe_ptr__ign: Source address in the address space
+ * @mm: Address space to copy from
+ * @flags: Reserved for future use; must be zero
+ *
+ * Copies data from the user address space associated with @mm. The destination
+ * is zeroed if an attempted copy cannot be completed in full. Unsupported
+ * flags return -EINVAL without modifying @dst.
+ *
+ * Return: 0 on success, -EINVAL if @flags is non-zero, or -EFAULT if @mm is no
+ * longer active or the copy fails or is partial.
+ */
+__bpf_kfunc int bpf_copy_from_user_mm(void *dst, u32 dst__sz,
+ const void __user *unsafe_ptr__ign,
+ struct mm_struct *mm, u64 flags)
+{
+ int ret;
+
+ if (unlikely(flags))
+ return -EINVAL;
+
+ if (unlikely(!dst__sz))
+ return 0;
+
+ if (unlikely(!mmget_not_zero(mm))) {
+ memset(dst, 0, dst__sz);
+ return -EFAULT;
+ }
+
+ ret = __bpf_copy_from_user_mm(dst, dst__sz, unsafe_ptr__ign, mm);
+ mmput(mm);
+
+ return ret;
+}
+
+/**
+ * bpf_copy_from_user_mm_str() - Copy a string from an address space
+ * @dst: Destination address, in kernel space. This buffer must be
+ * at least @dst__sz bytes long
+ * @dst__sz: Maximum number of bytes to copy, including the trailing NUL
+ * @unsafe_ptr__ign: Source address in the address space
+ * @mm: Address space to copy from
+ * @flags: The only supported flag is BPF_F_PAD_ZEROS
+ *
+ * Copies a NUL-terminated string from the user address space associated with
+ * @mm. If the string is too long, @dst is still NUL-terminated unless @dst__sz
+ * is zero.
+ *
+ * If the flags are valid and BPF_F_PAD_ZEROS is set, the unused portion of
+ * @dst is cleared on success and all of @dst is cleared on a copy failure.
+ * Unsupported flags return -EINVAL without modifying @dst.
+ *
+ * Return: The number of copied bytes including the NUL terminator on success,
+ * -EINVAL if @flags is invalid, or -EFAULT if @mm is no longer active or the
+ * copy fails.
+ */
+__bpf_kfunc int bpf_copy_from_user_mm_str(void *dst, u32 dst__sz,
+ const void __user *unsafe_ptr__ign,
+ struct mm_struct *mm, u64 flags)
+{
+ int ret;
+
+ if (unlikely(flags & ~BPF_F_PAD_ZEROS))
+ return -EINVAL;
+
+ if (unlikely(dst__sz == 0))
+ return 0;
+
+ if (unlikely(!mmget_not_zero(mm))) {
+ if (flags & BPF_F_PAD_ZEROS)
+ memset(dst, 0, dst__sz);
+ else
+ *(char *)dst = '\0';
+ return -EFAULT;
+ }
+
+ ret = __bpf_copy_from_user_mm_str(dst, dst__sz, unsafe_ptr__ign,
+ mm, flags);
+ mmput(mm);
+
+ return ret;
+}
+
+/**
* bpf_copy_from_user_task_str() - Copy a string from an task's address space
* @dst: Destination address, in kernel space. This buffer must be
* at least @dst__sz bytes long.
@@ -3681,6 +3809,7 @@ __bpf_kfunc int bpf_copy_from_user_task_str(void *dst, u32 dst__sz,
const void __user *unsafe_ptr__ign,
struct task_struct *tsk, u64 flags)
{
+ struct mm_struct *mm;
int ret;
if (unlikely(flags & ~BPF_F_PAD_ZEROS))
@@ -3689,17 +3818,20 @@ __bpf_kfunc int bpf_copy_from_user_task_str(void *dst, u32 dst__sz,
if (unlikely(dst__sz == 0))
return 0;
- ret = copy_remote_vm_str(tsk, (unsigned long)unsafe_ptr__ign, dst, dst__sz, 0);
- if (ret < 0) {
+ mm = get_task_mm(tsk);
+ if (!mm) {
if (flags & BPF_F_PAD_ZEROS)
memset(dst, 0, dst__sz);
- return ret;
+ else
+ *(char *)dst = '\0';
+ return -EFAULT;
}
- if (flags & BPF_F_PAD_ZEROS)
- memset(dst + ret, 0, dst__sz - ret);
+ ret = __bpf_copy_from_user_mm_str(dst, dst__sz, unsafe_ptr__ign,
+ mm, flags);
+ mmput(mm);
- return ret + 1;
+ return ret;
}
/* Keep unsigned long in prototype so that kfunc is usable when emitted to
@@ -4437,15 +4569,21 @@ static void bpf_task_work_ctx_put(struct bpf_task_work_ctx *ctx)
}
}
-static void bpf_task_work_cancel(struct bpf_task_work_ctx *ctx)
+static void bpf_task_work_cancel(struct bpf_task_work_ctx *ctx,
+ struct task_struct *task)
{
/*
* Scheduled task_work callback holds ctx ref, so if we successfully
* cancelled, we put that ref on callback's behalf. If we couldn't
* cancel, callback will inevitably run or has already completed
* running, and it would have taken care of its ctx ref itself.
+ *
+ * The caller passes the task explicitly: the scheduling handler must
+ * not read ctx->task after the work is published (the callback may
+ * have reset it), while in the asynchronous cancellation path
+ * ctx->task is stable, see bpf_task_work_cancel_scheduled().
*/
- if (task_work_cancel(ctx->task, &ctx->work))
+ if (task_work_cancel(task, &ctx->work))
bpf_task_work_ctx_put(ctx);
}
@@ -4486,6 +4624,7 @@ static void bpf_task_work_callback(struct callback_head *cb)
static void bpf_task_work_irq(struct irq_work *irq_work)
{
struct bpf_task_work_ctx *ctx = container_of(irq_work, struct bpf_task_work_ctx, irq_work);
+ struct task_struct *task;
enum bpf_task_work_state state;
int err;
@@ -4496,7 +4635,16 @@ static void bpf_task_work_irq(struct irq_work *irq_work)
return;
}
- err = task_work_add(ctx->task, &ctx->work, ctx->mode);
+ /*
+ * Capture the task pointer before the work is published: once the
+ * callback runs, it may reset ctx->task. The capture happens while
+ * this round's task reference is held, and every path that can
+ * release it runs after this handler entered its rcu read-side
+ * section. bpf_task_release() is put_task_struct_rcu_user(), so the
+ * task_struct itself cannot be freed before this section ends.
+ */
+ task = ctx->task;
+ err = task_work_add(task, &ctx->work, ctx->mode);
if (err) {
bpf_task_work_ctx_reset(ctx);
/*
@@ -4510,14 +4658,14 @@ static void bpf_task_work_irq(struct irq_work *irq_work)
/*
* It's technically possible for just scheduled task_work callback to
- * complete running by now, going SCHEDULING -> RUNNING and then
- * dropping its ctx refcount. Instead of capturing an extra ref just
- * to protect below ctx->state access, we rely on rcu_read_lock
- * above to prevent kfree_rcu from freeing ctx before we return.
+ * complete running by now, going SCHEDULING -> RUNNING, resetting
+ * ctx->task and dropping its ctx refcount. The rcu read-side section
+ * above keeps both the ctx memory and the captured task pointer
+ * valid until this handler returns.
*/
state = cmpxchg(&ctx->state, BPF_TW_SCHEDULING, BPF_TW_SCHEDULED);
if (state == BPF_TW_FREED)
- bpf_task_work_cancel(ctx); /* clean up if we switched into FREED state */
+ bpf_task_work_cancel(ctx, task); /* clean up if we switched into FREED state */
}
static struct bpf_task_work_ctx *bpf_task_work_fetch_ctx(struct bpf_task_work *tw,
@@ -4537,6 +4685,7 @@ static struct bpf_task_work_ctx *bpf_task_work_fetch_ctx(struct bpf_task_work *t
memset(ctx, 0, sizeof(*ctx));
refcount_set(&ctx->refcnt, 1); /* map's own ref */
ctx->state = BPF_TW_STANDBY;
+ init_irq_work(&ctx->irq_work, bpf_task_work_irq);
old_ctx = cmpxchg(&twk->ctx, NULL, ctx);
if (old_ctx) {
@@ -4579,6 +4728,18 @@ static struct bpf_task_work_ctx *bpf_task_work_acquire_ctx(struct bpf_task_work
bpf_task_work_ctx_put(ctx);
return ERR_PTR(-EBUSY);
}
+ /*
+ * STANDBY can be published before the previous scheduling irq_work
+ * returns. Claim PENDING before checking BUSY to order the check after
+ * that publication. No irq_work has been queued for this attempt yet,
+ * so reject reuse and roll back to STANDBY while BUSY is set. Preserve
+ * FREED if deletion wins the rollback race.
+ */
+ if (unlikely(irq_work_is_busy(&ctx->irq_work))) {
+ (void)cmpxchg(&ctx->state, BPF_TW_PENDING, BPF_TW_STANDBY);
+ bpf_task_work_ctx_put(ctx);
+ return ERR_PTR(-EBUSY);
+ }
/*
* If no process or bpffs is holding a reference to the map, no new callbacks should be
@@ -4628,7 +4789,6 @@ static int bpf_task_work_schedule(struct task_struct *task, struct bpf_task_work
ctx->map = map;
ctx->map_val = (void *)tw - map->record->task_work_off;
init_task_work(&ctx->work, bpf_task_work_callback);
- init_irq_work(&ctx->irq_work, bpf_task_work_irq);
irq_work_queue(&ctx->irq_work);
return 0;
@@ -4676,6 +4836,106 @@ __bpf_kfunc int bpf_task_work_schedule_resume(struct task_struct *task, struct b
return bpf_task_work_schedule(task, tw, map__const_map, callback, aux, TWA_RESUME);
}
+typedef int (*bpf_rcu_callback_t)(struct bpf_map *map, void *key, void *value);
+
+/* Actual type for struct bpf_rcu_head */
+struct bpf_rcu_head_kern {
+ struct rcu_head rcu;
+ bpf_callback_t callback_fn;
+ struct bpf_map *map;
+ struct bpf_prog *prog;
+ u32 armed;
+} __aligned(8);
+
+static void bpf_rcu_run_callback(struct rcu_head *rcu)
+{
+ struct bpf_rcu_head_kern *rh = container_of(rcu, struct bpf_rcu_head_kern, rcu);
+ bpf_callback_t callback_fn = rh->callback_fn;
+ struct bpf_prog *prog = rh->prog;
+ struct bpf_map *map = rh->map;
+ void *value, *key;
+ u32 idx;
+
+ value = (void *)rh - map->record->rcu_head_off;
+ key = map_key_from_value(map, value, &idx);
+
+ /* Pairs with the arming cmpxchg(): rh may be re-armed as soon as this store lands. */
+ smp_store_release(&rh->armed, 0);
+
+ rcu_read_lock_dont_migrate();
+ callback_fn((u64)(long)map, (u64)(long)key, (u64)(long)value, 0, 0);
+ rcu_read_unlock_migrate();
+
+ bpf_prog_put(prog);
+}
+
+static int __bpf_call_rcu(struct bpf_rcu_head *rh, struct bpf_map *map, void *callback,
+ struct bpf_prog_aux *aux, bool trace)
+{
+ struct bpf_rcu_head_kern *rhk = (void *)rh;
+ struct bpf_prog *prog;
+
+ BUILD_BUG_ON(sizeof(struct bpf_rcu_head_kern) > sizeof(struct bpf_rcu_head));
+ BUILD_BUG_ON(__alignof__(struct bpf_rcu_head_kern) != __alignof__(struct bpf_rcu_head));
+ BTF_TYPE_EMIT(struct bpf_rcu_head);
+
+ /* A queued callback cannot be cancelled, so a self-rearming one would pin prog and map. */
+ if (!atomic64_read(&map->usercnt))
+ return -EPERM;
+
+ if (cmpxchg(&rhk->armed, 0, 1))
+ return -EBUSY;
+
+ prog = bpf_prog_inc_not_zero(aux->prog);
+ if (IS_ERR(prog)) {
+ WRITE_ONCE(rhk->armed, 0);
+ return -EBADF;
+ }
+
+ rhk->callback_fn = (bpf_callback_t)callback;
+ rhk->map = map;
+ rhk->prog = prog;
+ if (trace)
+ call_rcu_tasks_trace(&rhk->rcu, bpf_rcu_run_callback);
+ else
+ call_rcu(&rhk->rcu, bpf_rcu_run_callback);
+ return 0;
+}
+
+/**
+ * bpf_call_rcu - Invoke a BPF callback after an RCU grace period
+ * @rh: struct bpf_rcu_head in a BPF map value
+ * @map__const_map: bpf_map that embeds struct bpf_rcu_head in the values
+ * @callback: BPF subprogram, invoked as callback(map, key, value) for the value holding @rh
+ * @aux: bpf_prog_aux of the caller, implicitly set by the verifier
+ *
+ * Return: 0, -EBUSY if @rh is already queued, -EPERM if @map is held by neither a process
+ * nor bpffs, or -EBADF if the calling program is going away.
+ */
+__bpf_kfunc int bpf_call_rcu(struct bpf_rcu_head *rh, void *map__const_map,
+ bpf_rcu_callback_t callback, struct bpf_prog_aux *aux)
+{
+ return __bpf_call_rcu(rh, map__const_map, callback, aux, false);
+}
+
+/**
+ * bpf_call_rcu_tasks_trace - Invoke a BPF callback after an RCU tasks trace grace period
+ * @rh: struct bpf_rcu_head in a BPF map value
+ * @map__const_map: bpf_map that embeds struct bpf_rcu_head in the values
+ * @callback: BPF subprogram, invoked as callback(map, key, value) for the value holding @rh
+ * @aux: bpf_prog_aux of the caller, implicitly set by the verifier
+ *
+ * Waits for sleepable BPF programs too. The callback itself is not sleepable either way.
+ *
+ * Return: 0, -EBUSY if @rh is already queued, -EPERM if @map is held by neither a process
+ * nor bpffs, or -EBADF if the calling program is going away.
+ */
+__bpf_kfunc int bpf_call_rcu_tasks_trace(struct bpf_rcu_head *rh, void *map__const_map,
+ bpf_rcu_callback_t callback, struct bpf_prog_aux *aux)
+{
+ return __bpf_call_rcu(rh, map__const_map, callback, aux, true);
+}
+
static int make_file_dynptr(struct file *file, u32 flags, bool may_sleep,
struct bpf_dynptr_kern *ptr)
{
@@ -4777,7 +5037,13 @@ static void bpf_task_work_cancel_scheduled(struct irq_work *irq_work)
{
struct bpf_task_work_ctx *ctx = container_of(irq_work, struct bpf_task_work_ctx, irq_work);
- bpf_task_work_cancel(ctx); /* this might put task_work callback's ref */
+ /*
+ * Deletion observed SCHEDULED and won against the callback's
+ * transition to RUNNING. The callback takes its FREED exit without
+ * resetting ctx->task, and the transferred map reference prevents
+ * destruction until cancellation completes.
+ */
+ bpf_task_work_cancel(ctx, ctx->task); /* this might put task_work callback's ref */
bpf_task_work_ctx_put(ctx); /* and here we put map's own ref that was transferred to us */
}
@@ -4924,6 +5190,8 @@ BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW | KF_PERFMON)
BTF_ID_FLAGS(func, bpf_iter_bits_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_bits_destroy, KF_ITER_DESTROY)
BTF_ID_FLAGS(func, bpf_copy_from_user_str, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_mm, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_mm_str, KF_SLEEPABLE | KF_PERFMON)
BTF_ID_FLAGS(func, bpf_copy_from_user_task_str, KF_SLEEPABLE | KF_PERFMON)
BTF_ID_FLAGS(func, bpf_get_kmem_cache, KF_PERFMON)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_new, KF_ITER_NEW | KF_SLEEPABLE)
@@ -4969,6 +5237,8 @@ BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE | KF_
BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE | KF_PERFMON)
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_IMPLICIT_ARGS)
+BTF_ID_FLAGS(func, bpf_call_rcu, KF_IMPLICIT_ARGS)
+BTF_ID_FLAGS(func, bpf_call_rcu_tasks_trace, KF_IMPLICIT_ARGS)
BTF_ID_FLAGS(func, bpf_dynptr_from_file)
BTF_ID_FLAGS(func, bpf_dynptr_file_discard, KF_RELEASE)
BTF_ID_FLAGS(func, bpf_timer_cancel_async)
diff --git a/kernel/bpf/keys.c b/kernel/bpf/keys.c
new file mode 100644
index 000000000000..bffc356839ac
--- /dev/null
+++ b/kernel/bpf/keys.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 Isovalent */
+
+#include <linux/bpf.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/key.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "bpf."
+
+static struct key *bpf_keyring;
+
+static bool bpf_keyring_unsealed __ro_after_init;
+module_param_named(keyring_unsealed, bpf_keyring_unsealed, bool, 0444);
+MODULE_PARM_DESC(keyring_unsealed, "Leave the bpf keyring unsealed");
+
+bool bpf_keyring_enforced(void)
+{
+ return bpf_keyring_unsealed;
+}
+
+struct bpf_key *bpf_lookup_keyring(void)
+{
+ struct bpf_key *bkey;
+
+ if (!bpf_keyring)
+ return NULL;
+ if (!READ_ONCE(bpf_keyring->keys.nr_leaves_on_tree) ||
+ !READ_ONCE(bpf_keyring->restrict_link))
+ return NULL;
+
+ bkey = kmalloc_obj(*bkey);
+ if (!bkey)
+ return NULL;
+
+ bkey->key = bpf_keyring;
+ bkey->has_ref = false;
+ return bkey;
+}
+
+static int __init bpf_keyring_init(void)
+{
+ struct key *keyring;
+
+ keyring = keyring_alloc(".bpf",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+ current_cred(), KEY_POS_SEARCH |
+ KEY_USR_VIEW | KEY_USR_READ |
+ KEY_USR_WRITE | KEY_USR_SEARCH |
+ KEY_USR_SETATTR, KEY_ALLOC_NOT_IN_QUOTA,
+ NULL, NULL);
+ if (IS_ERR(keyring)) {
+ pr_err("bpf: cannot allocate bpf keyring: %ld\n",
+ PTR_ERR(keyring));
+ return 0;
+ }
+ if (!bpf_keyring_unsealed &&
+ keyring_restrict(make_key_ref(keyring, true), NULL, NULL)) {
+ pr_err("bpf: cannot seal bpf keyring\n");
+ key_revoke(keyring);
+ key_put(keyring);
+ return 0;
+ }
+
+ bpf_keyring = keyring;
+ key_register_bpf_keyring(keyring);
+ return 0;
+}
+late_initcall(bpf_keyring_init);
diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c
index 301fc60bddc4..e657e6d95408 100644
--- a/kernel/bpf/liveness.c
+++ b/kernel/bpf/liveness.c
@@ -10,10 +10,40 @@
#define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args)
-struct per_frame_masks {
- spis_t may_read; /* stack slots that may be read by this instruction */
- spis_t must_write; /* stack slots written by this instruction */
- spis_t live_before; /* stack slots that may be read by this insn and its successors */
+/*
+ * Stack liveness is tracked with a 4-byte (half register) granularity.
+ * Half-slot 0 covers [fp-4, fp), half-slot 1 covers [fp-8, fp-4), and so on,
+ * hence FRAME_HALF_SPIS - 1 is the deepest half-slot a frame can have.
+ */
+#define FRAME_HALF_SPIS (MAX_BPF_STACK_JIT / BPF_HALF_REG_SIZE)
+#define FRAME_MAX_WORDS BITS_TO_LONGS(FRAME_HALF_SPIS)
+
+/* Masks tracked for each instruction of a frame */
+enum {
+ FM_MAY_READ, /* stack slots that may be read by this instruction */
+ FM_MUST_WRITE, /* stack slots written by this instruction */
+ FM_LIVE_BEFORE, /* stack slots that may be read by this insn and its successors */
+ FM_MAY_WRITE, /* stack slots that may be written by this instruction */
+ FM_MASK_CNT,
+};
+
+/*
+ * Per instruction stack masks for one frame of a function instance.
+ *
+ * Most frames use only a fraction of the stack budget, so instead of masks
+ * wide enough for every half-slot of the largest possible frame, all masks
+ * of one array share the width @words, and the marking functions widen the
+ * array as deeper half-slots are recorded. Mask @kind of the instruction at
+ * relative index @i is at &bits[(i * FM_MASK_CNT + kind) * words]. A
+ * half-slot at or past @words * BITS_PER_LONG is never read by this frame,
+ * hence never live. An instruction that may read the whole frame, such as a
+ * call passing a frame pointer to another subprog, widens the array to the
+ * program's stack budget, the deepest an accepted program can reach, so
+ * that the read cannot lose half-slots to a later widening.
+ */
+struct frame_masks {
+ u32 words;
+ unsigned long bits[];
};
/*
@@ -29,7 +59,7 @@ struct func_instance {
u32 subprog_start; /* cached env->subprog_info[subprog].start */
u32 insn_cnt; /* cached number of insns in the function */
/* Per frame, per instruction masks, frames allocated lazily. */
- struct per_frame_masks *frames[MAX_CALL_FRAMES];
+ struct frame_masks *frames[MAX_CALL_FRAMES];
bool must_write_initialized;
};
@@ -151,53 +181,145 @@ static int relative_idx(struct func_instance *instance, u32 insn_idx)
return insn_idx - instance->subprog_start;
}
-static struct per_frame_masks *get_frame_masks(struct func_instance *instance,
- u32 frame, u32 insn_idx)
+static u32 frame_mask_bits(struct frame_masks *fm)
{
- if (!instance->frames[frame])
- return NULL;
+ return fm->words * BITS_PER_LONG;
+}
+
+static size_t frame_mask_words(struct func_instance *instance, u32 words)
+{
+ return (size_t)instance->insn_cnt * FM_MASK_CNT * words;
+}
- return &instance->frames[frame][relative_idx(instance, insn_idx)];
+/* Mask @kind of the instruction at relative index @rel */
+static unsigned long *rel_mask(struct frame_masks *fm, u32 rel, u32 kind)
+{
+ return fm->bits + ((size_t)rel * FM_MASK_CNT + kind) * fm->words;
}
-static struct per_frame_masks *alloc_frame_masks(struct func_instance *instance,
- u32 frame, u32 insn_idx)
+/*
+ * Make sure @frame has a mask array at least @words wide, allocating it or
+ * copying the existing masks into the wider stride as needed.
+ * @words must be in range [1, FRAME_MAX_WORDS].
+ */
+static struct frame_masks *widen_frame_masks(struct func_instance *instance,
+ u32 frame, u32 words)
{
- struct per_frame_masks *arr;
+ struct frame_masks *old = instance->frames[frame], *new;
+ u32 i, kind;
- if (!instance->frames[frame]) {
- arr = kvzalloc_objs(*arr, instance->insn_cnt,
- GFP_KERNEL_ACCOUNT);
- instance->frames[frame] = arr;
- if (!arr)
- return ERR_PTR(-ENOMEM);
+ if (old && old->words >= words)
+ return old;
+ new = kvzalloc_flex(*new, bits, frame_mask_words(instance, words), GFP_KERNEL_ACCOUNT);
+ if (!new)
+ return NULL;
+ new->words = words;
+ if (old) {
+ for (i = 0; i < instance->insn_cnt; i++)
+ for (kind = 0; kind < FM_MASK_CNT; kind++)
+ memcpy(rel_mask(new, i, kind), rel_mask(old, i, kind),
+ old->words * sizeof(*old->bits));
+ kvfree(old);
}
- return get_frame_masks(instance, frame, insn_idx);
+ instance->frames[frame] = new;
+ return new;
}
-/* Accumulate may_read masks for @frame at @insn_idx */
-static int mark_stack_read(struct func_instance *instance, u32 frame, u32 insn_idx, spis_t mask)
+/*
+ * Set the inclusive half-slot range [lo, hi] in mask @kind of @frame at @insn_idx.
+ * An empty range, including one with a negative @hi as computed for a write
+ * that does not fully cover any half-slot, marks nothing.
+ */
+static int mark_stack_range(struct func_instance *instance, u32 frame, u32 insn_idx,
+ u32 kind, s32 lo, s32 hi)
{
- struct per_frame_masks *masks;
+ struct frame_masks *fm;
- masks = alloc_frame_masks(instance, frame, insn_idx);
- if (IS_ERR(masks))
- return PTR_ERR(masks);
- masks->may_read = spis_or(masks->may_read, mask);
+ /*
+ * An access past the frame bottom is rejected by the main verifier
+ * pass later, liveness only has to avoid running off the masks.
+ */
+ hi = min_t(s32, hi, FRAME_HALF_SPIS - 1);
+ if (lo > hi)
+ return 0;
+ fm = widen_frame_masks(instance, frame, BITS_TO_LONGS(hi + 1));
+ if (!fm)
+ return -ENOMEM;
+ bitmap_set(rel_mask(fm, relative_idx(instance, insn_idx), kind), lo, hi - lo + 1);
return 0;
}
-static int mark_stack_write(struct func_instance *instance, u32 frame, u32 insn_idx, spis_t mask)
+/* Accumulate may_read for half-slots [lo, hi] of @frame at @insn_idx */
+static int mark_stack_read(struct func_instance *instance, u32 frame, u32 insn_idx,
+ s32 lo, s32 hi)
{
- struct per_frame_masks *masks;
+ return mark_stack_range(instance, frame, insn_idx, FM_MAY_READ, lo, hi);
+}
- masks = alloc_frame_masks(instance, frame, insn_idx);
- if (IS_ERR(masks))
- return PTR_ERR(masks);
- masks->must_write = spis_or(masks->must_write, mask);
+/* Accumulate must_write for half-slots [lo, hi] of @frame at @insn_idx */
+static int mark_stack_write(struct func_instance *instance, u32 frame, u32 insn_idx,
+ s32 lo, s32 hi)
+{
+ return mark_stack_range(instance, frame, insn_idx, FM_MUST_WRITE, lo, hi);
+}
+
+static int mark_stack_may_write(struct func_instance *instance, u32 frame, u32 insn_idx,
+ s32 lo, s32 hi)
+{
+ return mark_stack_range(instance, frame, insn_idx, FM_MAY_WRITE, lo, hi);
+}
+
+/*
+ * Mark every half-slot of @frame as possibly read by @insn_idx. This widens
+ * the masks to the program's stack budget: a full read recorded at a narrower
+ * width would leave the bits added by a later widening clear and lose part of
+ * it. No verifier state holds a slot past the budget, so no liveness query
+ * reaches beyond it; an access past the budget in unreachable code may still
+ * widen a frame's masks further, which is harmless.
+ */
+static int mark_stack_read_all(struct bpf_verifier_env *env, struct func_instance *instance,
+ u32 frame, u32 insn_idx)
+{
+ return mark_stack_read(instance, frame, insn_idx, 0,
+ env->stack_limit / BPF_HALF_REG_SIZE - 1);
+}
+
+static int mark_stack_may_write_all(struct bpf_verifier_env *env, struct func_instance *instance,
+ u32 frame, u32 insn_idx)
+{
+ return mark_stack_may_write(instance, frame, insn_idx, 0,
+ env->stack_limit / BPF_HALF_REG_SIZE - 1);
+}
+
+/* Accumulate @src, a mask @src_words wide, into @kind mask of @frame at @insn_idx */
+static int mark_stack_mask(struct func_instance *instance, u32 frame, u32 insn_idx, u32 kind,
+ const unsigned long *src, u32 src_words)
+{
+ u32 nbits = src_words * BITS_PER_LONG;
+ struct frame_masks *fm;
+ unsigned long *dst;
+ u32 last, w;
+
+ last = find_last_bit(src, nbits);
+ if (last == nbits)
+ return 0;
+ fm = widen_frame_masks(instance, frame, BITS_TO_LONGS(last + 1));
+ if (!fm)
+ return -ENOMEM;
+ dst = rel_mask(fm, relative_idx(instance, insn_idx), kind);
+ /* @src has no bits set past @last, hence none past @fm->words either */
+ src_words = min(src_words, fm->words);
+ for (w = 0; w < src_words; w++)
+ dst[w] |= src[w];
return 0;
}
+static int mark_stack_read_mask(struct func_instance *instance, u32 frame, u32 insn_idx,
+ const unsigned long *src, u32 src_words)
+{
+ return mark_stack_mask(instance, frame, insn_idx, FM_MAY_READ, src, src_words);
+}
+
int bpf_jmp_offset(struct bpf_insn *insn)
{
u8 code = insn->code;
@@ -248,14 +370,18 @@ bpf_insn_successors(struct bpf_verifier_env *env, u32 idx)
struct bpf_iarray *succ, *jt;
int insn_sz;
- jt = env->insn_aux_data[idx].jt;
- if (unlikely(jt))
- return jt;
-
/* pre-allocated array of size up to 2; reset cnt, as it may have been used already */
succ = env->succ;
succ->cnt = 0;
+ /* All gotox of a subprogram share its jump table, see compute_subprog_jts() */
+ if (unlikely(insn_is_gotox(insn))) {
+ jt = bpf_find_containing_subprog(env, idx)->jt;
+ if (verifier_bug_if(!jt, env, "no jump table for gotox insn %u", idx))
+ return succ;
+ return jt;
+ }
+
opcode_info = &opcode_info_tbl[BPF_CLASS(insn->code) | BPF_OP(insn->code)];
insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
if (opcode_info->can_fallthrough)
@@ -264,6 +390,20 @@ bpf_insn_successors(struct bpf_verifier_env *env, u32 idx)
if (opcode_info->can_jump)
succ->items[succ->cnt++] = idx + bpf_jmp_offset(insn) + 1;
+ /*
+ * tail_call upon success and ld_{abs,ind} upon load failure return
+ * from the subprogram, which is a hidden edge to its exit. A subprogram
+ * without an exit insn has no exit to model.
+ */
+ if (unlikely((bpf_helper_call(insn) && insn->imm == BPF_FUNC_tail_call) ||
+ (BPF_CLASS(insn->code) == BPF_LD &&
+ (BPF_MODE(insn->code) == BPF_ABS || BPF_MODE(insn->code) == BPF_IND)))) {
+ u32 exit_idx = bpf_find_containing_subprog(env, idx)->exit_idx;
+
+ if (exit_idx != U32_MAX)
+ succ->items[succ->cnt++] = exit_idx;
+ }
+
return succ;
}
@@ -272,33 +412,42 @@ __diag_pop();
static inline bool update_insn(struct bpf_verifier_env *env,
struct func_instance *instance, u32 frame, u32 insn_idx)
{
- spis_t new_before, new_after;
- struct per_frame_masks *insn, *succ_insn;
+ unsigned long new_after[FRAME_MAX_WORDS] = {};
+ unsigned long *may_read, *must_write, *live_before;
+ struct frame_masks *fm = instance->frames[frame];
+ u32 rel = relative_idx(instance, insn_idx);
struct bpf_iarray *succ;
- u32 s;
- bool changed;
+ bool changed = false;
+ u32 s, w;
succ = bpf_insn_successors(env, insn_idx);
if (succ->cnt == 0)
return false;
- changed = false;
- insn = get_frame_masks(instance, frame, insn_idx);
- new_before = SPIS_ZERO;
- new_after = SPIS_ZERO;
+ /* All instructions of one frame array share the same mask width */
for (s = 0; s < succ->cnt; ++s) {
- succ_insn = get_frame_masks(instance, frame, succ->items[s]);
- new_after = spis_or(new_after, succ_insn->live_before);
+ unsigned long *succ_live;
+
+ succ_live = rel_mask(fm, relative_idx(instance, succ->items[s]), FM_LIVE_BEFORE);
+ for (w = 0; w < fm->words; w++)
+ new_after[w] |= succ_live[w];
}
+ may_read = rel_mask(fm, rel, FM_MAY_READ);
+ must_write = rel_mask(fm, rel, FM_MUST_WRITE);
+ live_before = rel_mask(fm, rel, FM_LIVE_BEFORE);
/*
* New "live_before" is a union of all "live_before" of successors
* minus slots written by instruction plus slots read by instruction.
* new_before = (new_after & ~insn->must_write) | insn->may_read
*/
- new_before = spis_or(spis_and(new_after, spis_not(insn->must_write)),
- insn->may_read);
- changed |= !spis_equal(new_before, insn->live_before);
- insn->live_before = new_before;
+ for (w = 0; w < fm->words; w++) {
+ unsigned long new_before = (new_after[w] & ~must_write[w]) | may_read[w];
+
+ if (new_before != live_before[w]) {
+ live_before[w] = new_before;
+ changed = true;
+ }
+ }
return changed;
}
@@ -329,10 +478,12 @@ static void update_instance(struct bpf_verifier_env *env, struct func_instance *
static bool is_live_before(struct func_instance *instance, u32 insn_idx, u32 frameno, u32 half_spi)
{
- struct per_frame_masks *masks;
+ struct frame_masks *fm = instance->frames[frameno];
- masks = get_frame_masks(instance, frameno, insn_idx);
- return masks && spis_test_bit(masks->live_before, half_spi);
+ /* No recorded access reaches past the masks, so nothing there is live */
+ if (!fm || half_spi >= frame_mask_bits(fm))
+ return false;
+ return test_bit(half_spi, rel_mask(fm, relative_idx(instance, insn_idx), FM_LIVE_BEFORE));
}
int bpf_live_stack_query_init(struct bpf_verifier_env *env, struct bpf_verifier_state *st)
@@ -356,12 +507,29 @@ int bpf_live_stack_query_init(struct bpf_verifier_env *env, struct bpf_verifier_
return 0;
}
+/*
+ * Stack slots of outer frames that the callee may read are accounted as read
+ * by the @callsite insn itself, i.e. they are live before @callsite and may be
+ * dead after it, when:
+ * - @callsite calls a callback, which may be called several times;
+ * - @callsite is callx. Its callee is not known when stack liveness is
+ * computed, so there is no func instance for it at @callsite. While
+ * the verifier is in the callee lookup_instance() finds its standalone
+ * instance that knows nothing about outer frames. What the callee may read
+ * there is recorded by record_call_access().
+ */
+static bool callee_stack_access_at_callsite(struct bpf_verifier_env *env, u32 callsite)
+{
+ return bpf_calls_callback(env, callsite) ||
+ bpf_is_callx(&env->prog->insnsi[callsite]);
+}
+
bool bpf_stack_slot_alive(struct bpf_verifier_env *env, u32 frameno, u32 half_spi)
{
/*
* Slot is alive if it is read before q->insn_idx in current func instance,
* or if for some outer func instance:
- * - alive before callsite if callsite calls callback, otherwise
+ * - alive before callsite if callsite calls callback or is callx, otherwise
* - alive after callsite
*/
struct live_stack_query *q = &env->liveness->live_stack_query;
@@ -394,7 +562,7 @@ bool bpf_stack_slot_alive(struct bpf_verifier_env *env, u32 frameno, u32 half_sp
/* Get callsite from verifier state, not from instance callchain */
callsite = q->callsites[i];
- alive = bpf_calls_callback(env, callsite)
+ alive = callee_stack_access_at_callsite(env, callsite)
? is_live_before(instance, callsite, rel, half_spi)
: is_live_before(instance, callsite + 1, rel, half_spi);
if (alive)
@@ -430,17 +598,19 @@ static int spi_off(int spi)
* When only one half is set, print as "-4h","-8h",...
* Runs of 3+ consecutive fully-set SPIs are collapsed: "fp0-8..-24"
*/
-static char *fmt_spis_mask(struct bpf_verifier_env *env, int frame, bool first, spis_t spis)
+static char *fmt_spis_mask(struct bpf_verifier_env *env, int frame, bool first,
+ const unsigned long *spis, u32 words)
{
int buf_sz = sizeof(env->tmp_str_buf);
+ int spi_cnt = words * BITS_PER_LONG / 2;
char *buf = env->tmp_str_buf;
int spi, n, run_start;
buf[0] = '\0';
- for (spi = 0; spi < STACK_SLOTS / 2 && buf_sz > 0; spi++) {
- bool lo = spis_test_bit(spis, spi * 2);
- bool hi = spis_test_bit(spis, spi * 2 + 1);
+ for (spi = 0; spi < spi_cnt && buf_sz > 0; spi++) {
+ bool lo = test_bit(spi * 2, spis);
+ bool hi = test_bit(spi * 2 + 1, spis);
const char *space = first ? "" : " ";
if (!lo && !hi)
@@ -450,16 +620,16 @@ static char *fmt_spis_mask(struct bpf_verifier_env *env, int frame, bool first,
/* half-spi */
n = scnprintf(buf, buf_sz, "%sfp%d%d%s",
space, frame, spi_off(spi) + (lo ? STACK_SLOT_SZ : 0), "h");
- } else if (spi + 2 < STACK_SLOTS / 2 &&
- spis_test_bit(spis, spi * 2 + 2) &&
- spis_test_bit(spis, spi * 2 + 3) &&
- spis_test_bit(spis, spi * 2 + 4) &&
- spis_test_bit(spis, spi * 2 + 5)) {
+ } else if (spi + 2 < spi_cnt &&
+ test_bit(spi * 2 + 2, spis) &&
+ test_bit(spi * 2 + 3, spis) &&
+ test_bit(spi * 2 + 4, spis) &&
+ test_bit(spi * 2 + 5, spis)) {
/* 3+ consecutive full spis */
run_start = spi;
- while (spi + 1 < STACK_SLOTS / 2 &&
- spis_test_bit(spis, (spi + 1) * 2) &&
- spis_test_bit(spis, (spi + 1) * 2 + 1))
+ while (spi + 1 < spi_cnt &&
+ test_bit((spi + 1) * 2, spis) &&
+ test_bit((spi + 1) * 2 + 1, spis))
spi++;
n = scnprintf(buf, buf_sz, "%sfp%d%d..%d",
space, frame, spi_off(run_start), spi_off(spi));
@@ -474,15 +644,41 @@ static char *fmt_spis_mask(struct bpf_verifier_env *env, int frame, bool first,
return env->tmp_str_buf;
}
+/* Print mask @kind of the instruction at relative index @i for every frame, if any bit is set. */
+static bool print_mask(struct bpf_verifier_env *env, struct func_instance *instance, int i,
+ const char *name, u32 kind)
+{
+ struct frame_masks *fm;
+ bool printed = false;
+ unsigned long *mask;
+ int frame;
+ u64 pos;
+
+ pos = env->log.end_pos;
+ verbose(env, "%s", name);
+ for (frame = instance->depth; frame >= 0; --frame) {
+ fm = instance->frames[frame];
+ if (!fm)
+ continue;
+ mask = rel_mask(fm, i, kind);
+ if (bitmap_empty(mask, frame_mask_bits(fm)))
+ continue;
+ verbose(env, "%s", fmt_spis_mask(env, frame, !printed, mask, fm->words));
+ printed = true;
+ }
+ if (!printed)
+ bpf_vlog_reset(&env->log, pos);
+ return printed;
+}
+
static void print_instance(struct bpf_verifier_env *env, struct func_instance *instance)
{
int start = env->subprog_info[instance->subprog].start;
struct bpf_insn *insns = env->prog->insnsi;
- struct per_frame_masks *masks;
int len = instance->insn_cnt;
- int insn_idx, frame, i;
- bool has_use, has_def;
u64 pos, insn_pos;
+ int insn_idx, i;
+ bool printed;
if (!(env->log.level & BPF_LOG_LEVEL2))
return;
@@ -491,35 +687,17 @@ static void print_instance(struct bpf_verifier_env *env, struct func_instance *i
verbose(env, "%s:\n", fmt_instance(env, instance));
for (i = 0; i < len; i++) {
insn_idx = start + i;
- has_use = false;
- has_def = false;
pos = env->log.end_pos;
verbose(env, "%3d: ", insn_idx);
bpf_verbose_insn(env, &insns[insn_idx]);
insn_pos = env->log.end_pos;
verbose(env, "%*c;", bpf_vlog_alignment(insn_pos - pos), ' ');
- pos = env->log.end_pos;
- verbose(env, " use: ");
- for (frame = instance->depth; frame >= 0; --frame) {
- masks = get_frame_masks(instance, frame, insn_idx);
- if (!masks || spis_is_zero(masks->may_read))
- continue;
- verbose(env, "%s", fmt_spis_mask(env, frame, !has_use, masks->may_read));
- has_use = true;
- }
- if (!has_use)
- bpf_vlog_reset(&env->log, pos);
- pos = env->log.end_pos;
- verbose(env, " def: ");
- for (frame = instance->depth; frame >= 0; --frame) {
- masks = get_frame_masks(instance, frame, insn_idx);
- if (!masks || spis_is_zero(masks->must_write))
- continue;
- verbose(env, "%s", fmt_spis_mask(env, frame, !has_def, masks->must_write));
- has_def = true;
- }
- if (!has_def)
- bpf_vlog_reset(&env->log, has_use ? pos : insn_pos);
+ printed = false;
+ printed |= print_mask(env, instance, i, " use: ", FM_MAY_READ);
+ printed |= print_mask(env, instance, i, " def: ", FM_MUST_WRITE);
+ printed |= print_mask(env, instance, i, " may_def: ", FM_MAY_WRITE);
+ if (!printed)
+ bpf_vlog_reset(&env->log, insn_pos);
verbose(env, "\n");
if (bpf_is_ldimm64(&insns[insn_idx]))
i++;
@@ -540,6 +718,49 @@ static int cmp_instances(const void *pa, const void *pb)
return 0;
}
+/* OR the 8-byte slots touched by a half-slot (4-byte) mask into @slots. */
+static void half_spis_to_slots(unsigned long *slots, const unsigned long *mask, u32 nbits)
+{
+ u32 slot;
+
+ for (slot = 0; slot < MAX_BPF_STACK_SLOTS && slot * 2 + 1 < nbits; slot++)
+ if (test_bit(slot * 2, mask) || test_bit(slot * 2 + 1, mask))
+ __set_bit(slot, slots);
+}
+
+/*
+ * Precompute, for each instruction, the OR of may_write and live_before masks
+ * over its top frame across all func_instances reaching it, stash them in the
+ * insn_aux_data.
+ */
+static void compute_may_write_masks(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_liveness *liveness = env->liveness;
+ struct func_instance *instance;
+ struct frame_masks *fm;
+ u32 nbits;
+ int bkt, i;
+
+ hash_for_each(liveness->func_instances, bkt, instance, hl_node) {
+ fm = instance->frames[instance->depth];
+ if (!fm)
+ continue;
+ nbits = frame_mask_bits(fm);
+ for (i = 0; i < instance->insn_cnt; i++) {
+ half_spis_to_slots(aux[instance->subprog_start + i].may_write_mask,
+ rel_mask(fm, i, FM_MAY_WRITE), nbits);
+ half_spis_to_slots(aux[instance->subprog_start + i].live_stack_before,
+ rel_mask(fm, i, FM_LIVE_BEFORE), nbits);
+ }
+ }
+}
+
+const unsigned long *bpf_may_write_mask(struct bpf_verifier_env *env, u32 insn_idx)
+{
+ return env->insn_aux_data[insn_idx].may_write_mask;
+}
+
/* print use/def slots for all instances ordered by callsite first, then by depth */
static int print_instances(struct bpf_verifier_env *env)
{
@@ -584,9 +805,9 @@ static int print_instances(struct bpf_verifier_env *env)
* - same frame + different offset -> offset-imprecise
* - different frames -> fully-imprecise (bitmask OR)
*
- * At memory access sites (LDX/STX/ST), offset-imprecise marks only
- * the known frame's access mask as SPIS_ALL, while fully-imprecise
- * iterates bits in the bitmask and routes each frame to its target.
+ * At memory access sites (LDX/STX/ST), offset-imprecise marks the known
+ * frame as fully read, while fully-imprecise iterates bits in the bitmask
+ * and routes each frame to its target.
*/
#define MAX_ARG_OFFSETS 4
@@ -605,8 +826,15 @@ enum arg_track_state {
ARG_IMPRECISE = -3, /* lost identity; .mask is arg bitmask */
};
-/* Track callee stack slots fp-8 through fp-512 (64 slots of 8 bytes each) */
-#define MAX_ARG_SPILL_SLOTS 64
+/*
+ * The tracker follows as many spill slots as a subprog addresses directly
+ * through R10, see subprog_spill_slots(). A frame's slots at a call site are
+ * kept in a snapshot for the callees that receive pointers into that frame.
+ */
+struct spill_snapshot {
+ u32 slots;
+ struct arg_track at[];
+};
/*
* Combined register + stack arg tracking: R0-R10 at indices 0-10,
@@ -881,13 +1109,60 @@ static void arg_padd(struct arg_track *at, s64 delta)
}
/*
- * Convert a byte offset from FP to a callee stack slot index.
+ * Slots the spill tracker may follow for a subprog of @len instructions
+ * without its per-instruction tables costing more than they could for the
+ * largest program while every frame stayed within MAX_BPF_STACK: as many
+ * entries as 64 slots need for BPF_COMPLEXITY_LIMIT_INSNS instructions.
+ */
+static u32 spill_slots_affordable(int len)
+{
+ u32 base = MAX_BPF_STACK / BPF_REG_SIZE;
+
+ return max_t(u32, base, base * BPF_COMPLEXITY_LIMIT_INSNS / len);
+}
+
+/*
+ * Number of 8-byte spill slots to track for the instructions in [@start, @end):
+ * the 64 slots of a MAX_BPF_STACK frame, which the tracker has always
+ * followed, or the deepest 8-byte stack access made directly through R10 when
+ * that reaches further, up to the program's stack budget. Spills and fills are
+ * compiled as direct R10 accesses, so the count covers them whatever the frame
+ * size; a fill through a derived pointer that reaches past it is treated as
+ * imprecise, as one past the frame always was.
+ */
+static u32 subprog_spill_slots(struct bpf_verifier_env *env, int start, int end)
+{
+ struct bpf_insn *insns = env->prog->insnsi;
+ int deepest = 0;
+
+ for (int i = start; i < end; i++) {
+ struct bpf_insn *insn = &insns[i];
+ u8 class = BPF_CLASS(insn->code);
+ u8 base;
+
+ if (BPF_SIZE(insn->code) != BPF_DW)
+ continue;
+ if (class == BPF_LDX)
+ base = insn->src_reg;
+ else if (class == BPF_STX || class == BPF_ST)
+ base = insn->dst_reg;
+ else
+ continue;
+ if (base == BPF_REG_FP && insn->off < 0)
+ deepest = max(deepest, -insn->off);
+ }
+ return clamp_t(u32, DIV_ROUND_UP(deepest, 8), MAX_BPF_STACK / BPF_REG_SIZE,
+ env->stack_limit / BPF_REG_SIZE);
+}
+
+/*
+ * Convert a byte offset from FP to one of the @nslots tracked stack slots.
* Returns -1 if out of range or not 8-byte aligned.
* Slot 0 = fp-8, slot 1 = fp-16, ..., slot 7 = fp-64, ....
*/
-static int fp_off_to_slot(s16 off)
+static int fp_off_to_slot(s16 off, u32 nslots)
{
- if (off >= 0 || off < -(int)(MAX_ARG_SPILL_SLOTS * 8))
+ if (off >= 0 || off < -(int)(nslots * 8))
return -1;
if (off % 8)
return -1;
@@ -896,7 +1171,7 @@ static int fp_off_to_slot(s16 off)
static struct arg_track fill_from_stack(struct bpf_insn *insn,
struct arg_track *at_out, int reg,
- struct arg_track *at_stack_out,
+ struct arg_track *at_stack_out, u32 nslots,
int depth)
{
struct arg_track imp = {
@@ -907,7 +1182,7 @@ static struct arg_track fill_from_stack(struct bpf_insn *insn,
int cnt, i;
if (reg == BPF_REG_FP) {
- int slot = fp_off_to_slot(insn->off);
+ int slot = fp_off_to_slot(insn->off, nslots);
return slot >= 0 ? at_stack_out[slot] : imp;
}
@@ -920,7 +1195,7 @@ static struct arg_track fill_from_stack(struct bpf_insn *insn,
if (arg_add(at_out[reg].off[i], insn->off, &fp_off))
return imp;
- slot = fp_off_to_slot(fp_off);
+ slot = fp_off_to_slot(fp_off, nslots);
if (slot < 0)
return imp;
result = __arg_track_join(result, at_stack_out[slot]);
@@ -935,7 +1210,7 @@ static struct arg_track fill_from_stack(struct bpf_insn *insn,
* When exact offset is unknown conservatively add reg values to all slots in at_stack_out.
*/
static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out,
- int reg, struct arg_track *at_stack_out,
+ int reg, struct arg_track *at_stack_out, u32 nslots,
struct arg_track *val, u32 sz)
{
struct arg_track none = { .frame = ARG_NONE };
@@ -943,7 +1218,7 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out,
int cnt, i;
if (reg == BPF_REG_FP) {
- int slot = fp_off_to_slot(insn->off);
+ int slot = fp_off_to_slot(insn->off, nslots);
if (slot >= 0)
at_stack_out[slot] = new_val;
@@ -951,7 +1226,7 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out,
}
cnt = at_out[reg].off_cnt;
if (cnt == 0) {
- for (int slot = 0; slot < MAX_ARG_SPILL_SLOTS; slot++)
+ for (int slot = 0; slot < nslots; slot++)
at_stack_out[slot] = __arg_track_join(at_stack_out[slot], new_val);
return;
}
@@ -961,7 +1236,7 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out,
if (arg_add(at_out[reg].off[i], insn->off, &fp_off))
continue;
- slot = fp_off_to_slot(fp_off);
+ slot = fp_off_to_slot(fp_off, nslots);
if (slot < 0)
continue;
if (cnt == 1)
@@ -975,16 +1250,17 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out,
* Clear all tracked callee stack slots overlapping the byte range
* [off, off+sz-1] where off is a negative FP-relative offset.
*/
-static void clear_overlapping_stack_slots(struct arg_track *at_stack, s16 off, u32 sz, int cnt)
+static void clear_overlapping_stack_slots(struct arg_track *at_stack, u32 nslots, s16 off,
+ u32 sz, int cnt)
{
struct arg_track none = { .frame = ARG_NONE };
if (cnt == 0) {
- for (int i = 0; i < MAX_ARG_SPILL_SLOTS; i++)
+ for (int i = 0; i < nslots; i++)
at_stack[i] = __arg_track_join(at_stack[i], none);
return;
}
- for (int i = 0; i < MAX_ARG_SPILL_SLOTS; i++) {
+ for (int i = 0; i < nslots; i++) {
int slot_start = -((i + 1) * 8);
int slot_end = slot_start + 8;
@@ -1002,33 +1278,33 @@ static void clear_overlapping_stack_slots(struct arg_track *at_stack, s16 off, u
*/
static void clear_stack_for_all_offs(struct bpf_insn *insn,
struct arg_track *at_out, int reg,
- struct arg_track *at_stack_out, u32 sz)
+ struct arg_track *at_stack_out, u32 nslots, u32 sz)
{
int cnt, i;
if (reg == BPF_REG_FP) {
- clear_overlapping_stack_slots(at_stack_out, insn->off, sz, 1);
+ clear_overlapping_stack_slots(at_stack_out, nslots, insn->off, sz, 1);
return;
}
cnt = at_out[reg].off_cnt;
if (cnt == 0) {
- clear_overlapping_stack_slots(at_stack_out, 0, sz, cnt);
+ clear_overlapping_stack_slots(at_stack_out, nslots, 0, sz, cnt);
return;
}
for (i = 0; i < cnt; i++) {
s16 fp_off;
if (arg_add(at_out[reg].off[i], insn->off, &fp_off)) {
- clear_overlapping_stack_slots(at_stack_out, 0, sz, 0);
+ clear_overlapping_stack_slots(at_stack_out, nslots, 0, sz, 0);
break;
}
- clear_overlapping_stack_slots(at_stack_out, fp_off, sz, cnt);
+ clear_overlapping_stack_slots(at_stack_out, nslots, fp_off, sz, cnt);
}
}
static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, int idx,
struct arg_track *at_in, struct arg_track *at_stack_in,
- struct arg_track *at_out, struct arg_track *at_stack_out)
+ struct arg_track *at_out, struct arg_track *at_stack_out, u32 nslots)
{
bool printed = false;
int i;
@@ -1060,7 +1336,7 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i
verbose(env, "\tsa%d: ", i); verbose_arg_track(env, &at_in[ai]);
verbose(env, " -> "); verbose_arg_track(env, &at_out[ai]);
}
- for (i = 0; i < MAX_ARG_SPILL_SLOTS; i++) {
+ for (i = 0; i < nslots; i++) {
if (arg_track_eq(&at_stack_out[i], &at_stack_in[i]))
continue;
if (!printed) {
@@ -1082,13 +1358,36 @@ static bool can_be_local_fp(int depth, int regno, struct arg_track *at)
}
/*
+ * Model 8-byte reads through @reg, including load-acquire
+ * and the old value fetched by atomic operations.
+ */
+static struct arg_track arg_track_deref(struct bpf_verifier_env *env, struct bpf_insn *insn,
+ struct arg_track *at_out, int reg,
+ struct arg_track *at_stack_out, u32 nslots,
+ int depth, u32 *callsites)
+{
+ struct arg_track *ptr = &at_out[reg];
+
+ if (can_be_local_fp(depth, reg, ptr))
+ return fill_from_stack(insn, at_out, reg, at_stack_out, nslots, depth);
+ if (ptr->frame >= 0 && ptr->frame < depth) {
+ struct spill_snapshot *parent = env->callsite_at_stack[callsites[ptr->frame]];
+
+ return fill_from_stack(insn, at_out, reg, parent->at, parent->slots, ptr->frame);
+ }
+ if (ptr->frame == ARG_IMPRECISE && !(ptr->mask & BIT(depth)) && ptr->mask)
+ return *ptr;
+ return (struct arg_track){ .frame = ARG_NONE };
+}
+
+/*
* Pure dataflow transfer function for arg_track state.
* Updates at_out[] based on how the instruction modifies registers.
* Tracks spill/fill, but not other memory accesses.
*/
static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn,
int insn_idx,
- struct arg_track *at_out, struct arg_track *at_stack_out,
+ struct arg_track *at_out, struct arg_track *at_stack_out, u32 nslots,
const struct arg_track *at_stack_arg_entry,
struct func_instance *instance,
u32 *callsites)
@@ -1162,55 +1461,42 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn,
for (r = BPF_REG_0; r <= BPF_REG_5; r++)
at_out[r] = none;
} else if (class == BPF_LDX) {
- u32 sz = bpf_size_to_bytes(BPF_SIZE(insn->code));
- bool src_is_local_fp = can_be_local_fp(depth, insn->src_reg, src);
-
- /*
- * Reload from callee stack: if src is current-frame FP-derived
- * and the load is an 8-byte BPF_MEM, try to restore the spill
- * identity. For imprecise sources fill_from_stack() returns
- * ARG_IMPRECISE (off_cnt == 0).
- */
- if (src_is_local_fp && BPF_MODE(insn->code) == BPF_MEM && sz == 8) {
- *dst = fill_from_stack(insn, at_out, insn->src_reg, at_stack_out, depth);
- } else if (src->frame >= 0 && src->frame < depth &&
- BPF_MODE(insn->code) == BPF_MEM && sz == 8) {
- struct arg_track *parent_stack =
- env->callsite_at_stack[callsites[src->frame]];
-
- *dst = fill_from_stack(insn, at_out, insn->src_reg,
- parent_stack, src->frame);
- } else if (src->frame == ARG_IMPRECISE &&
- !(src->mask & BIT(depth)) && src->mask &&
- BPF_MODE(insn->code) == BPF_MEM && sz == 8) {
- /*
- * Imprecise src with only parent-frame bits:
- * conservative fallback.
- */
- *dst = *src;
- } else {
+ /* Only an 8-byte BPF_MEM load can be a fill, see arg_track_deref(). */
+ if (BPF_MODE(insn->code) == BPF_MEM && BPF_SIZE(insn->code) == BPF_DW)
+ *dst = arg_track_deref(env, insn, at_out, insn->src_reg,
+ at_stack_out, nslots, depth, callsites);
+ else
*dst = none;
- }
} else if (class == BPF_LD && BPF_MODE(insn->code) == BPF_IMM) {
*dst = none;
} else if (class == BPF_STX) {
u32 sz = bpf_size_to_bytes(BPF_SIZE(insn->code));
- bool dst_is_local_fp;
-
- /* Track spills to current-frame FP-derived callee stack */
- dst_is_local_fp = can_be_local_fp(depth, insn->dst_reg, dst);
- if (dst_is_local_fp && BPF_MODE(insn->code) == BPF_MEM)
- spill_to_stack(insn, at_out, insn->dst_reg,
- at_stack_out, src, sz);
-
- if (BPF_MODE(insn->code) == BPF_ATOMIC) {
- if (dst_is_local_fp && insn->imm != BPF_LOAD_ACQ)
- clear_stack_for_all_offs(insn, at_out, insn->dst_reg,
- at_stack_out, sz);
+ bool dst_is_local_fp = can_be_local_fp(depth, insn->dst_reg, dst);
+ u8 mode = BPF_MODE(insn->code);
+
+ if (mode == BPF_MEM || (mode == BPF_ATOMIC && insn->imm == BPF_STORE_REL)) {
+ if (dst_is_local_fp)
+ spill_to_stack(insn, at_out, insn->dst_reg,
+ at_stack_out, nslots, src, sz);
+ } else if (mode == BPF_ATOMIC && insn->imm == BPF_LOAD_ACQ) {
+ if (sz == 8)
+ *dst = arg_track_deref(env, insn, at_out, insn->src_reg,
+ at_stack_out, nslots, depth, callsites);
+ else
+ *dst = none;
+ } else if (mode == BPF_ATOMIC) {
+ /* BPF_XCHG, BPF_CMPXCHG or BPF_{ADD,AND,OR,XOR} | [BPF_FETCH] */
+ struct arg_track old = none;
r = bpf_atomic_load_reg(insn);
+ if (r >= 0 && sz == 8)
+ old = arg_track_deref(env, insn, at_out, insn->dst_reg,
+ at_stack_out, nslots, depth, callsites);
+ if (dst_is_local_fp)
+ clear_stack_for_all_offs(insn, at_out, insn->dst_reg,
+ at_stack_out, nslots, sz);
if (r >= 0)
- at_out[r] = none;
+ at_out[r] = old;
}
} else if (class == BPF_ST && BPF_MODE(insn->code) == BPF_MEM) {
u32 sz = bpf_size_to_bytes(BPF_SIZE(insn->code));
@@ -1219,23 +1505,21 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn,
/* BPF_ST to FP-derived dst: clear overlapping stack slots */
if (dst_is_local_fp)
clear_stack_for_all_offs(insn, at_out, insn->dst_reg,
- at_stack_out, sz);
+ at_stack_out, nslots, sz);
}
}
/*
- * Record access_bytes from helper/kfunc or load/store insn.
- * access_bytes > 0: stack read
- * access_bytes < 0: stack write
- * access_bytes == S64_MIN: unknown — conservative, mark [0..slot] as read
- * access_bytes == 0: no access
- *
+ * Record possible reads and writes for every touched half-slot. A definite
+ * write requires full coverage, a known size, and a single possible offset.
*/
-static int record_stack_access_off(struct func_instance *instance, s64 fp_off,
- s64 access_bytes, u32 frame, u32 insn_idx)
+static int record_stack_access_off(struct func_instance *instance, const struct arg_track *arg,
+ s64 off_idx, struct arg_access_info info,
+ u32 frame, u32 insn_idx)
{
+ s64 fp_off = arg->off[off_idx];
s32 slot_hi, slot_lo;
- spis_t mask;
+ int err;
if (fp_off >= 0)
/*
@@ -1244,68 +1528,68 @@ static int record_stack_access_off(struct func_instance *instance, s64 fp_off,
* by the main verifier pass later.
*/
return 0;
- if (access_bytes == S64_MIN) {
- /* helper/kfunc read unknown amount of bytes from fp_off until fp+0 */
- slot_hi = (-fp_off - 1) / STACK_SLOT_SZ;
- mask = SPIS_ZERO;
- spis_or_range(&mask, 0, slot_hi);
- return mark_stack_read(instance, frame, insn_idx, mask);
- }
- if (access_bytes > 0) {
- /* Mark any touched slot as use */
- slot_hi = (-fp_off - 1) / STACK_SLOT_SZ;
- slot_lo = max_t(s32, (-fp_off - access_bytes) / STACK_SLOT_SZ, 0);
- mask = SPIS_ZERO;
- spis_or_range(&mask, slot_lo, slot_hi);
- return mark_stack_read(instance, frame, insn_idx, mask);
- } else if (access_bytes < 0) {
+
+ /*
+ * Read and may_write marks include partially covered slots.
+ * An unknown access size may reach from fp_off to the frame top.
+ */
+ slot_hi = (-fp_off - 1) / STACK_SLOT_SZ;
+ slot_lo = info.size == U32_MAX
+ ? 0
+ : max_t(s32, (-fp_off - info.size) / STACK_SLOT_SZ, 0);
+ if (info.may_read) {
+ err = mark_stack_read(instance, frame, insn_idx, slot_lo, slot_hi);
+ if (err)
+ return err;
+ }
+ if (info.may_write) {
+ err = mark_stack_may_write(instance, frame, insn_idx, slot_lo, slot_hi);
+ if (err)
+ return err;
+ }
+ if (info.must_write && info.size != U32_MAX && arg->off_cnt == 1) {
/* Mark only fully covered slots as def */
- access_bytes = -access_bytes;
slot_hi = (-fp_off) / STACK_SLOT_SZ - 1;
- slot_lo = max_t(s32, (-fp_off - access_bytes + STACK_SLOT_SZ - 1) / STACK_SLOT_SZ, 0);
- if (slot_lo <= slot_hi) {
- mask = SPIS_ZERO;
- spis_or_range(&mask, slot_lo, slot_hi);
- return mark_stack_write(instance, frame, insn_idx, mask);
- }
+ slot_lo = max_t(s32, (-fp_off - info.size + STACK_SLOT_SZ - 1) / STACK_SLOT_SZ, 0);
+ return mark_stack_write(instance, frame, insn_idx, slot_lo, slot_hi);
}
return 0;
}
-/*
- * 'arg' is FP-derived argument to helper/kfunc or load/store that
- * reads (positive) or writes (negative) 'access_bytes' into 'use' or 'def'.
- */
-static int record_stack_access(struct func_instance *instance,
+/* Record access through a pointer with a known frame and possibly known offsets. */
+static int record_stack_access(struct bpf_verifier_env *env,
+ struct func_instance *instance,
const struct arg_track *arg,
- s64 access_bytes, u32 frame, u32 insn_idx)
+ struct arg_access_info info, u32 frame, u32 insn_idx)
{
int i, err;
- if (access_bytes == 0)
+ if (!info.size)
return 0;
if (arg->off_cnt == 0) {
- if (access_bytes > 0 || access_bytes == S64_MIN)
- return mark_stack_read(instance, frame, insn_idx, SPIS_ALL);
+ if (info.may_read) {
+ err = mark_stack_read_all(env, instance, frame, insn_idx);
+ if (err)
+ return err;
+ }
+ if (info.may_write) {
+ err = mark_stack_may_write_all(env, instance, frame, insn_idx);
+ if (err)
+ return err;
+ }
return 0;
}
- if (access_bytes != S64_MIN && access_bytes < 0 && arg->off_cnt != 1)
- /* multi-offset write cannot set stack_def */
- return 0;
-
for (i = 0; i < arg->off_cnt; i++) {
- err = record_stack_access_off(instance, arg->off[i], access_bytes, frame, insn_idx);
+ err = record_stack_access_off(instance, arg, i, info, frame, insn_idx);
if (err)
return err;
}
return 0;
}
-/*
- * When a pointer is ARG_IMPRECISE, conservatively mark every frame in
- * the bitmask as fully used.
- */
-static int record_imprecise(struct func_instance *instance, u32 mask, u32 insn_idx)
+/* Record possible effects on every candidate frame of an imprecise pointer. */
+static int record_imprecise(struct bpf_verifier_env *env, struct func_instance *instance,
+ struct arg_access_info info, u32 mask, u32 insn_idx)
{
int depth = instance->depth;
int f, err;
@@ -1314,9 +1598,16 @@ static int record_imprecise(struct func_instance *instance, u32 mask, u32 insn_i
if (!(mask & 1))
continue;
if (f <= depth) {
- err = mark_stack_read(instance, f, insn_idx, SPIS_ALL);
- if (err)
- return err;
+ if (info.may_read) {
+ err = mark_stack_read_all(env, instance, f, insn_idx);
+ if (err)
+ return err;
+ }
+ if (info.may_write) {
+ err = mark_stack_may_write_all(env, instance, f, insn_idx);
+ if (err)
+ return err;
+ }
}
}
return 0;
@@ -1328,9 +1619,10 @@ static int record_load_store_access(struct bpf_verifier_env *env,
struct arg_track *at, int insn_idx)
{
struct bpf_insn *insn = &env->prog->insnsi[insn_idx];
+ struct arg_access_info info = {};
int depth = instance->depth;
- s32 sz = bpf_size_to_bytes(BPF_SIZE(insn->code));
u8 class = BPF_CLASS(insn->code);
+ bool read = false, write = false;
struct arg_track resolved, *ptr;
int oi;
@@ -1347,23 +1639,34 @@ static int record_load_store_access(struct bpf_verifier_env *env,
switch (class) {
case BPF_LDX:
ptr = &at[insn->src_reg];
+ read = true;
break;
case BPF_STX:
if (BPF_MODE(insn->code) == BPF_ATOMIC) {
- if (insn->imm == BPF_STORE_REL)
- sz = -sz;
- if (insn->imm == BPF_LOAD_ACQ)
+ switch (insn->imm) {
+ case BPF_LOAD_ACQ:
ptr = &at[insn->src_reg];
- else
+ read = true;
+ break;
+ case BPF_STORE_REL:
+ ptr = &at[insn->dst_reg];
+ write = true;
+ break;
+ default:
+ /* ADD/AND/OR/XOR(+FETCH), XCHG, CMPXCHG */
ptr = &at[insn->dst_reg];
+ read = true;
+ write = true;
+ break;
+ }
} else {
ptr = &at[insn->dst_reg];
- sz = -sz;
+ write = true;
}
break;
case BPF_ST:
ptr = &at[insn->dst_reg];
- sz = -sz;
+ write = true;
break;
default:
return 0;
@@ -1382,10 +1685,14 @@ static int record_load_store_access(struct bpf_verifier_env *env,
ptr = &resolved;
}
+ info.may_read = read;
+ info.may_write = write;
+ info.must_write = write;
+ info.size = bpf_size_to_bytes(BPF_SIZE(insn->code));
if (ptr->frame >= 0 && ptr->frame <= depth)
- return record_stack_access(instance, ptr, sz, ptr->frame, insn_idx);
+ return record_stack_access(env, instance, ptr, info, ptr->frame, insn_idx);
if (ptr->frame == ARG_IMPRECISE)
- return record_imprecise(instance, ptr->mask, insn_idx);
+ return record_imprecise(env, instance, info, ptr->mask, insn_idx);
/* ARG_NONE: not derived from any frame pointer, skip */
return 0;
}
@@ -1396,37 +1703,42 @@ static int record_arg_access(struct bpf_verifier_env *env,
struct arg_track *at, int arg_idx,
int insn_idx)
{
+ struct arg_access_info info;
int depth = instance->depth;
int frame = at->frame;
int err = 0;
- s64 bytes;
if (!arg_is_fp(at))
return 0;
if (bpf_helper_call(insn)) {
- bytes = bpf_helper_stack_access_bytes(env, insn, arg_idx, insn_idx);
+ info = bpf_helper_stack_access_bytes(env, insn, arg_idx, insn_idx);
} else if (bpf_pseudo_kfunc_call(insn)) {
- bytes = bpf_kfunc_stack_access_bytes(env, insn, arg_idx, insn_idx);
+ info = bpf_kfunc_stack_access_bytes(env, insn, arg_idx, insn_idx);
+ } else if (bpf_pseudo_call(insn)) {
+ info = bpf_global_subprog_stack_access_bytes(env, insn, arg_idx, insn_idx);
} else {
for (int f = 0; f <= depth; f++) {
- err = mark_stack_read(instance, f, insn_idx, SPIS_ALL);
+ err = mark_stack_read_all(env, instance, f, insn_idx);
+ if (err)
+ return err;
+ err = mark_stack_may_write_all(env, instance, f, insn_idx);
if (err)
return err;
}
return 0;
}
- if (bytes == 0)
+ if (!info.size)
return 0;
if (frame >= 0 && frame <= depth)
- err = record_stack_access(instance, at, bytes, frame, insn_idx);
+ err = record_stack_access(env, instance, at, info, frame, insn_idx);
else if (frame == ARG_IMPRECISE)
- err = record_imprecise(instance, at->mask, insn_idx);
+ err = record_imprecise(env, instance, info, at->mask, insn_idx);
return err;
}
-/* Record stack access for a given 'at' state of helper/kfunc 'insn' */
+/* Record stack access for a given 'at' state of helper/kfunc/global subprog 'insn' */
static int record_call_access(struct bpf_verifier_env *env,
struct func_instance *instance,
struct arg_track *at,
@@ -1434,21 +1746,36 @@ static int record_call_access(struct bpf_verifier_env *env,
{
struct bpf_insn *insn = &env->prog->insnsi[insn_idx];
struct bpf_call_summary cs;
- int r, err, num_params = 5;
-
- if (bpf_pseudo_call(insn))
- return 0;
+ int r, err, callee, arg_slot_cnt = 5;
- if (bpf_get_call_summary(env, insn, &cs))
- num_params = cs.num_params;
+ if (bpf_pseudo_call(insn)) {
+ /*
+ * analyze_subprog() handles static callees. For global callees,
+ * derive effects from argument types so may_write marks remain
+ * conservative under freplace.
+ */
+ callee = bpf_find_subprog(env, insn_idx + insn->imm + 1);
+ if (callee < 0 || !bpf_subprog_is_global(env, callee))
+ return 0;
+ arg_slot_cnt = MAX_BPF_FUNC_ARGS;
+ } else if (bpf_is_callx(insn))
+ /*
+ * The callee is not known statically. Assume that all arg
+ * slots are passed and let record_arg_access() conservatively
+ * mark the stack of all frames as read if any of them is
+ * derived from a frame pointer.
+ */
+ arg_slot_cnt = MAX_BPF_FUNC_REG_ARGS + MAX_STACK_ARG_SLOTS;
+ else if (bpf_get_call_summary(env, insn, &cs))
+ arg_slot_cnt = cs.arg_slot_cnt;
- for (r = BPF_REG_1; r < BPF_REG_1 + min(num_params, MAX_BPF_FUNC_REG_ARGS); r++) {
+ for (r = BPF_REG_1; r < BPF_REG_1 + min(arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS); r++) {
err = record_arg_access(env, instance, insn, &at[r], r - 1, insn_idx);
if (err)
return err;
}
- for (r = 0; r < MAX_STACK_ARG_SLOTS && r < num_params - MAX_BPF_FUNC_REG_ARGS; r++) {
+ for (r = 0; r < MAX_STACK_ARG_SLOTS && r < arg_slot_cnt - MAX_BPF_FUNC_REG_ARGS; r++) {
err = record_arg_access(env, instance, insn, &at[MAX_BPF_REG + r],
r + MAX_BPF_FUNC_REG_ARGS, insn_idx);
if (err)
@@ -1517,7 +1844,7 @@ struct subprog_at_info {
static void print_subprog_arg_access(struct bpf_verifier_env *env,
int subprog,
struct subprog_at_info *info,
- struct arg_track (*at_stack_in)[MAX_ARG_SPILL_SLOTS])
+ struct arg_track *at_stack_in, u32 nslots)
{
struct bpf_insn *insns = env->prog->insnsi;
int start = env->subprog_info[subprog].start;
@@ -1533,7 +1860,7 @@ static void print_subprog_arg_access(struct bpf_verifier_env *env,
bool has_extra = false;
u8 cls = BPF_CLASS(insns[idx].code);
bool is_ldx_stx_call = cls == BPF_LDX || cls == BPF_STX ||
- insns[idx].code == (BPF_JMP | BPF_CALL);
+ (cls == BPF_JMP && BPF_OP(insns[idx].code) == BPF_CALL);
verbose(env, "%3d: ", idx);
bpf_verbose_insn(env, &insns[idx]);
@@ -1550,8 +1877,8 @@ static void print_subprog_arg_access(struct bpf_verifier_env *env,
has_extra = true;
}
if (is_ldx_stx_call) {
- for (r = 0; r < MAX_ARG_SPILL_SLOTS; r++)
- if (arg_is_fp(&at_stack_in[i][r]))
+ for (r = 0; r < nslots; r++)
+ if (arg_is_fp(&at_stack_in[(size_t)i * nslots + r]))
has_extra = true;
}
@@ -1581,11 +1908,11 @@ static void print_subprog_arg_access(struct bpf_verifier_env *env,
}
if (is_ldx_stx_call) {
- for (r = 0; r < MAX_ARG_SPILL_SLOTS; r++) {
- if (!arg_is_fp(&at_stack_in[i][r]))
+ for (r = 0; r < nslots; r++) {
+ if (!arg_is_fp(&at_stack_in[(size_t)i * nslots + r]))
continue;
verbose(env, " fp%+d=", -(r + 1) * 8);
- verbose_arg_track(env, &at_stack_in[i][r]);
+ verbose_arg_track(env, &at_stack_in[(size_t)i * nslots + r]);
}
}
@@ -1595,6 +1922,17 @@ static void print_subprog_arg_access(struct bpf_verifier_env *env,
}
}
+static void record_stack_ptrs(struct bpf_verifier_env *env, int idx, struct arg_track *at_in)
+{
+ u16 r, mask = 0;
+
+ for (r = 0; r < MAX_BPF_REG; r++)
+ if (arg_is_fp(&at_in[r]))
+ mask |= BIT(r);
+
+ env->insn_aux_data[idx].stack_ptrs |= mask;
+}
+
/*
* Compute arg tracking dataflow for a single subprog.
* Runs forward fixed-point with arg_track_xfer(), then records
@@ -1618,9 +1956,10 @@ static int compute_subprog_args(struct bpf_verifier_env *env,
int end = env->subprog_info[subprog + 1].start;
int po_end = env->subprog_info[subprog + 1].postorder_start;
int len = end - start;
+ u32 nslots = min(subprog_spill_slots(env, start, end), spill_slots_affordable(len));
struct arg_track (*at_in)[MAX_AT_TRACK_REGS] = NULL;
struct arg_track at_out[MAX_AT_TRACK_REGS];
- struct arg_track (*at_stack_in)[MAX_ARG_SPILL_SLOTS] = NULL;
+ struct arg_track *at_stack_in = NULL;
struct arg_track *at_stack_out = NULL;
struct arg_track at_stack_arg_entry[MAX_STACK_ARG_SLOTS];
struct arg_track unvisited = { .frame = ARG_UNVISITED };
@@ -1632,19 +1971,19 @@ static int compute_subprog_args(struct bpf_verifier_env *env,
if (!at_in)
goto err_free;
- at_stack_in = kvmalloc_objs(*at_stack_in, len, GFP_KERNEL_ACCOUNT);
+ at_stack_in = kvmalloc_objs(*at_stack_in, (size_t)len * nslots, GFP_KERNEL_ACCOUNT);
if (!at_stack_in)
goto err_free;
- at_stack_out = kvmalloc_objs(*at_stack_out, MAX_ARG_SPILL_SLOTS, GFP_KERNEL_ACCOUNT);
+ at_stack_out = kvmalloc_objs(*at_stack_out, nslots, GFP_KERNEL_ACCOUNT);
if (!at_stack_out)
goto err_free;
for (i = 0; i < len; i++) {
for (r = 0; r < MAX_AT_TRACK_REGS; r++)
at_in[i][r] = unvisited;
- for (r = 0; r < MAX_ARG_SPILL_SLOTS; r++)
- at_stack_in[i][r] = unvisited;
+ for (r = 0; r < nslots; r++)
+ at_stack_in[(size_t)i * nslots + r] = unvisited;
}
for (r = 0; r < MAX_AT_TRACK_REGS; r++)
@@ -1660,8 +1999,8 @@ static int compute_subprog_args(struct bpf_verifier_env *env,
}
/* Entry: all stack slots are ARG_NONE */
- for (r = 0; r < MAX_ARG_SPILL_SLOTS; r++)
- at_stack_in[0][r] = none;
+ for (r = 0; r < nslots; r++)
+ at_stack_in[r] = none;
/* Entry: incoming stack args from caller, or ARG_NONE for main */
for (r = 0; r < MAX_STACK_ARG_SLOTS; r++)
@@ -1683,11 +2022,12 @@ redo:
continue;
memcpy(at_out, at_in[i], sizeof(at_out));
- memcpy(at_stack_out, at_stack_in[i], MAX_ARG_SPILL_SLOTS * sizeof(*at_stack_out));
+ memcpy(at_stack_out, &at_stack_in[(size_t)i * nslots], nslots * sizeof(*at_stack_out));
- arg_track_xfer(env, insn, idx, at_out, at_stack_out,
+ arg_track_xfer(env, insn, idx, at_out, at_stack_out, nslots,
at_stack_arg_entry, instance, callsites);
- arg_track_log(env, insn, idx, at_in[i], at_stack_in[i], at_out, at_stack_out);
+ arg_track_log(env, insn, idx, at_in[i], &at_stack_in[(size_t)i * nslots], at_out,
+ at_stack_out, nslots);
/* Propagate to successors within this subprogram */
succ = bpf_insn_successors(env, idx);
@@ -1704,9 +2044,10 @@ redo:
changed |= arg_track_join(env, idx, target, r,
&at_in[ti][r], at_out[r]);
- for (r = 0; r < MAX_ARG_SPILL_SLOTS; r++)
+ for (r = 0; r < nslots; r++)
changed |= arg_track_join(env, idx, target, -r - 1,
- &at_stack_in[ti][r], at_stack_out[r]);
+ &at_stack_in[(size_t)ti * nslots + r],
+ at_stack_out[r]);
}
}
if (changed)
@@ -1722,30 +2063,33 @@ redo:
if (err)
goto err_free;
- if (insn->code == (BPF_JMP | BPF_CALL)) {
+ if (BPF_CLASS(insn->code) == BPF_JMP && BPF_OP(insn->code) == BPF_CALL) {
err = record_call_access(env, instance, at_in[i], idx);
if (err)
goto err_free;
}
if (bpf_pseudo_call(insn) || bpf_calls_callback(env, idx)) {
+ struct spill_snapshot *snap;
+
kvfree(env->callsite_at_stack[idx]);
- env->callsite_at_stack[idx] =
- kvmalloc_objs(*env->callsite_at_stack[idx],
- MAX_ARG_SPILL_SLOTS, GFP_KERNEL_ACCOUNT);
- if (!env->callsite_at_stack[idx]) {
+ env->callsite_at_stack[idx] = kvmalloc_flex(*snap, at, nslots, GFP_KERNEL_ACCOUNT);
+ snap = env->callsite_at_stack[idx];
+ if (!snap) {
err = -ENOMEM;
goto err_free;
}
- memcpy(env->callsite_at_stack[idx],
- at_stack_in[i], sizeof(struct arg_track) * MAX_ARG_SPILL_SLOTS);
+ snap->slots = nslots;
+ memcpy(snap->at, &at_stack_in[(size_t)i * nslots], nslots * sizeof(*snap->at));
}
+
+ record_stack_ptrs(env, idx, at_in[i]);
}
info->at_in = at_in;
at_in = NULL;
info->len = len;
- print_subprog_arg_access(env, subprog, info, at_stack_in);
+ print_subprog_arg_access(env, subprog, info, at_stack_in, nslots);
err = 0;
err_free:
@@ -1770,38 +2114,87 @@ static bool has_fp_args(struct arg_track *args)
/*
* Merge a freshly analyzed instance into the original.
* may_read: union (any pass might read the slot).
+ * may_write: union (slots written on ANY pass).
* must_write: intersection (only slots written on ALL passes are guaranteed).
* live_before is recomputed by a subsequent update_instance() on @dst.
+ *
+ * The two instances may have settled on different mask widths for the same
+ * frame, so @dst is widened to cover @src first. A word only @dst has counts
+ * as zero on the @src side: it unions into may_read as a no-op and intersects
+ * must_write to empty.
*/
-static void merge_instances(struct func_instance *dst, struct func_instance *src)
+static int merge_instances(struct func_instance *dst, struct func_instance *src)
{
- int f, i;
+ struct frame_masks *d, *s;
+ u32 f, i, w;
for (f = 0; f <= dst->depth; f++) {
- if (!src->frames[f]) {
+ s = src->frames[f];
+ d = dst->frames[f];
+ if (!s) {
/* This pass didn't touch frame f — must_write intersects with empty. */
- if (dst->frames[f])
+ if (d)
for (i = 0; i < dst->insn_cnt; i++)
- dst->frames[f][i].must_write = SPIS_ZERO;
+ bitmap_zero(rel_mask(d, i, FM_MUST_WRITE),
+ frame_mask_bits(d));
continue;
}
- if (!dst->frames[f]) {
+ if (!d) {
/* Previous pass didn't touch frame f — take src, zero must_write. */
- dst->frames[f] = src->frames[f];
+ dst->frames[f] = s;
src->frames[f] = NULL;
for (i = 0; i < dst->insn_cnt; i++)
- dst->frames[f][i].must_write = SPIS_ZERO;
+ bitmap_zero(rel_mask(s, i, FM_MUST_WRITE), frame_mask_bits(s));
continue;
}
+ d = widen_frame_masks(dst, f, s->words);
+ if (!d)
+ return -ENOMEM;
for (i = 0; i < dst->insn_cnt; i++) {
- dst->frames[f][i].may_read =
- spis_or(dst->frames[f][i].may_read,
- src->frames[f][i].may_read);
- dst->frames[f][i].must_write =
- spis_and(dst->frames[f][i].must_write,
- src->frames[f][i].must_write);
+ unsigned long *dst_read = rel_mask(d, i, FM_MAY_READ);
+ unsigned long *dst_write = rel_mask(d, i, FM_MUST_WRITE);
+ unsigned long *dst_may_write = rel_mask(d, i, FM_MAY_WRITE);
+ unsigned long *src_read = rel_mask(s, i, FM_MAY_READ);
+ unsigned long *src_write = rel_mask(s, i, FM_MUST_WRITE);
+ unsigned long *src_may_write = rel_mask(s, i, FM_MAY_WRITE);
+
+ for (w = 0; w < d->words; w++) {
+ dst_read[w] |= w < s->words ? src_read[w] : 0;
+ dst_write[w] &= w < s->words ? src_write[w] : 0;
+ dst_may_write[w] |= w < s->words ? src_may_write[w] : 0;
+ }
}
}
+ return 0;
+}
+
+/*
+ * Fold a fully analyzed callee instance writes to upper frames as
+ * may_write marks at callsite in caller's frames.
+ */
+static int merge_may_write(struct func_instance *caller, struct func_instance *callee)
+{
+ DECLARE_BITMAP(acc, FRAME_HALF_SPIS);
+ u32 call_idx = callee->callsite;
+ struct frame_masks *fm;
+ u32 f, i, nbits;
+ int err;
+
+ for (f = 0; f < callee->depth; f++) {
+ fm = callee->frames[f];
+ if (!fm)
+ continue;
+ nbits = frame_mask_bits(fm);
+ bitmap_zero(acc, nbits);
+ for (i = 0; i < callee->insn_cnt; i++)
+ bitmap_or(acc, acc, rel_mask(fm, i, FM_MAY_WRITE), nbits);
+ if (bitmap_empty(acc, nbits))
+ continue;
+ err = mark_stack_mask(caller, f, call_idx, FM_MAY_WRITE, acc, fm->words);
+ if (err)
+ return err;
+ }
+ return 0;
}
static struct func_instance *fresh_instance(struct func_instance *src)
@@ -1898,7 +2291,12 @@ static int analyze_subprog(struct bpf_verifier_env *env,
if (bpf_pseudo_call(insn)) {
target = idx + insn->imm + 1;
callee = bpf_find_subprog(env, target);
- if (callee < 0)
+ /*
+ * Global subprograms can be freplaced, so conservatively derive
+ * their stack effects from their BTF signatures instead of
+ * analyzing them in the context of their callers.
+ */
+ if (callee < 0 || bpf_subprog_is_global(env, callee))
continue;
/* Build entry args: R1-R5 and stack args from at_in at call site */
@@ -1916,7 +2314,10 @@ static int analyze_subprog(struct bpf_verifier_env *env,
if (info[subprog].at_in[j][caller_reg].frame == ARG_NONE)
continue;
for (int f = 0; f <= depth; f++) {
- err = mark_stack_read(instance, f, idx, SPIS_ALL);
+ err = mark_stack_read_all(env, instance, f, idx);
+ if (err)
+ goto out_free;
+ err = mark_stack_may_write_all(env, instance, f, idx);
if (err)
goto out_free;
}
@@ -1955,23 +2356,35 @@ static int analyze_subprog(struct bpf_verifier_env *env,
/* Pull callee's entry liveness back to caller's callsite */
{
u32 callee_start = callee_instance->subprog_start;
- struct per_frame_masks *entry;
+ struct frame_masks *callee_fm;
for (int f = 0; f < callee_instance->depth; f++) {
- entry = get_frame_masks(callee_instance, f, callee_start);
- if (!entry)
+ callee_fm = callee_instance->frames[f];
+ if (!callee_fm)
continue;
- err = mark_stack_read(instance, f, idx, entry->live_before);
+ err = mark_stack_read_mask(instance, f, idx,
+ rel_mask(callee_fm,
+ relative_idx(callee_instance,
+ callee_start),
+ FM_LIVE_BEFORE),
+ callee_fm->words);
if (err)
goto out_free;
}
}
+
+ /* Summarize callee's writes to ancestor frames onto the callsite */
+ err = merge_may_write(instance, callee_instance);
+ if (err)
+ goto out_free;
}
if (prev_instance) {
- merge_instances(prev_instance, instance);
+ err = merge_instances(prev_instance, instance);
free_instance(instance);
instance = prev_instance;
+ if (err)
+ return err;
}
update_instance(env, instance);
return 0;
@@ -2026,6 +2439,8 @@ int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env)
goto out;
}
+ compute_may_write_masks(env);
+
if (env->log.level & BPF_LOG_LEVEL2)
err = print_instances(env);
@@ -2057,10 +2472,17 @@ static inline u32 mask_widen(u32 m) { return m | (m << 16); }
static inline u16 mask_lo(u32 m) { return (u16)m; }
static inline u16 mask_hi(u32 m) { return (u16)(m >> 16); }
+/* Index of the subprogram called by pseudo call @insn */
+static int callee_subprog(struct bpf_verifier_env *env, const struct bpf_insn *insn)
+{
+ return bpf_find_subprog(env, insn - env->prog->insnsi + insn->imm + 1);
+}
+
/* Compute info->{use,def} fields for the instruction */
static void compute_insn_live_regs(struct bpf_verifier_env *env,
struct bpf_insn *insn,
- struct insn_live_regs *info)
+ struct insn_live_regs *info,
+ bool ret_reg_pair)
{
struct bpf_call_summary cs;
const u8 class = BPF_CLASS(insn->code);
@@ -2072,6 +2494,8 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
const u32 src32 = mask_lo(src);
const u32 dst32 = mask_lo(dst);
const u32 r0 = reg64_mask(0);
+ const u32 r2 = reg64_mask(BPF_REG_2);
+ int subprog, reg_args;
u32 def = 0;
u32 use = U32_MAX;
@@ -2191,15 +2615,28 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
break;
case BPF_EXIT:
def = 0;
- use = r0;
+ use = ret_reg_pair ? (r0 | r2) : r0;
break;
case BPF_CALL:
def = ALL_CALLER_SAVED_REGS;
use = def & ~BIT(BPF_REG_0);
- if (bpf_get_call_summary(env, insn, &cs))
- use = GENMASK(min_t(u8, cs.num_params, MAX_BPF_FUNC_REG_ARGS), 1);
+ if (bpf_get_call_summary(env, insn, &cs)) {
+ use = GENMASK(min_t(u8, cs.arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS), 1);
+ } else if (bpf_pseudo_call(insn)) {
+ /* a static callee's reads are added in bpf_compute_live_registers() */
+ subprog = callee_subprog(env, insn);
+ if (bpf_subprog_is_global(env, subprog)) {
+ reg_args = min_t(u8, env->subprog_info[subprog].arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS);
+ use = GENMASK(reg_args, 1);
+ } else {
+ use = 0;
+ }
+ }
def = mask_widen(def);
use = mask_widen(use);
+ /* callx reads the address of the callee from dst_reg */
+ if (bpf_is_callx(insn))
+ use |= dst;
break;
default:
def = 0;
@@ -2227,9 +2664,8 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
struct bpf_insn *insns = env->prog->insnsi;
struct insn_live_regs *state;
int insn_cnt = env->prog->len;
- u64 pos, insn_pos;
- int err = 0, i, j;
- bool changed;
+ int err = 0, i, subprog, start, end;
+ bool changed, ret_reg_pair;
/* Use the following algorithm:
* - define the following:
@@ -2256,8 +2692,14 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
goto out;
}
- for (i = 0; i < insn_cnt; ++i)
- compute_insn_live_regs(env, &insns[i], &state[i]);
+ for (subprog = 0; subprog < env->subprog_cnt; subprog++) {
+ start = env->subprog_info[subprog].start;
+ end = env->subprog_info[subprog + 1].start;
+ ret_reg_pair = bpf_ret_reg_pair(env, subprog);
+
+ for (i = start; i < end; ++i)
+ compute_insn_live_regs(env, &insns[i], &state[i], ret_reg_pair);
+ }
/* Forward pass: resolve stack access through FP-derived pointers */
err = bpf_compute_subprog_arg_access(env);
@@ -2273,11 +2715,21 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
struct bpf_iarray *succ;
u32 new_out = 0;
u32 new_in = 0;
+ u32 use;
succ = bpf_insn_successors(env, insn_idx);
for (int s = 0; s < succ->cnt; ++s)
new_out |= state[succ->items[s]].in;
- new_in = (new_out & ~live->def) | live->use;
+ use = live->use;
+ /*
+ * For a call to a static subprogram include the callee's
+ * entry 'in' as computed so far.
+ */
+ if (bpf_pseudo_call(&insns[insn_idx]) &&
+ !bpf_subprog_is_global(env, callee_subprog(env, &insns[insn_idx])))
+ use |= state[insn_idx + insns[insn_idx].imm + 1].in &
+ mask_widen(GENMASK(BPF_REG_5, BPF_REG_1));
+ new_in = (new_out & ~live->def) | use;
if (new_out != live->out || new_in != live->in) {
live->in = new_in;
live->out = new_out;
@@ -2301,31 +2753,6 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
insn_aux[i].zext_dst = def32 >= 0 && (mask_hi(out) & BIT(def32));
}
- if (env->log.level & BPF_LOG_LEVEL2) {
- verbose(env, "Live regs before insn:\n");
- for (i = 0; i < insn_cnt; ++i) {
- if (env->insn_aux_data[i].scc)
- verbose(env, "%3d ", env->insn_aux_data[i].scc);
- else
- verbose(env, " ");
- verbose(env, "%3d: ", i);
- for (j = BPF_REG_0; j < BPF_REG_10; ++j)
- if (insn_aux[i].live_regs_before & BIT(j))
- verbose(env, "%d", j);
- else
- verbose(env, ".");
- verbose(env, " ");
- pos = env->log.end_pos;
- bpf_verbose_insn(env, &insns[i]);
- insn_pos = env->log.end_pos;
- if (insn_aux[i].zext_dst)
- verbose(env, "%*c; zext", bpf_vlog_alignment(insn_pos - pos), ' ');
- verbose(env, "\n");
- if (bpf_is_ldimm64(&insns[i]))
- i++;
- }
- }
-
out:
kvfree(state);
return err;
diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
index fb032dfdc0de..5432c0fc442b 100644
--- a/kernel/bpf/log.c
+++ b/kernel/bpf/log.c
@@ -527,7 +527,7 @@ static void verbose_unum(struct bpf_verifier_env *env, u64 num)
verbose(env, "%#llx", num);
}
-static void verbose_snum(struct bpf_verifier_env *env, s64 num)
+void bpf_verbose_snum(struct bpf_verifier_env *env, s64 num)
{
if (is_snum_decimal(num))
verbose(env, "%lld", num);
@@ -609,7 +609,7 @@ static void print_scalar_ranges(struct bpf_verifier_env *env,
}
if (m1->name[0] == 's')
- verbose_snum(env, m1->val);
+ bpf_verbose_snum(env, m1->val);
else
verbose_unum(env, m1->val);
}
@@ -632,7 +632,7 @@ static void print_reg_state(struct bpf_verifier_env *env,
if (t == SCALAR_VALUE && reg->precise)
verbose(env, "P");
if (t == SCALAR_VALUE && tnum_is_const(reg->var_off)) {
- verbose_snum(env, reg->var_off.value);
+ bpf_verbose_snum(env, reg->var_off.value);
return;
}
@@ -643,7 +643,7 @@ static void print_reg_state(struct bpf_verifier_env *env,
if (state->frameno != reg->frameno)
verbose(env, "[%d]", reg->frameno);
if (tnum_is_const(reg->var_off)) {
- verbose_snum(env, reg->var_off.value + reg->delta);
+ bpf_verbose_snum(env, reg->var_off.value + reg->delta);
return;
}
}
@@ -667,7 +667,7 @@ static void print_reg_state(struct bpf_verifier_env *env,
}
if (t != SCALAR_VALUE && reg->delta) {
verbose_a("off=");
- verbose_snum(env, reg->delta);
+ bpf_verbose_snum(env, reg->delta);
}
if (type_is_pkt_pointer(t)) {
verbose_a("r=");
@@ -683,7 +683,7 @@ static void print_reg_state(struct bpf_verifier_env *env,
/* a pointer register with fixed offset */
if (reg->var_off.value) {
verbose_a("imm=");
- verbose_snum(env, reg->var_off.value);
+ bpf_verbose_snum(env, reg->var_off.value);
}
} else {
print_scalar_ranges(env, reg, &sep);
@@ -693,6 +693,8 @@ static void print_reg_state(struct bpf_verifier_env *env,
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
verbose_a("var_off=%s", tn_buf);
}
+ if (reg->base != 0 || reg->step != 1)
+ verbose_a("step=%d+%d", reg->base, reg->step);
}
verbose(env, ")");
}
@@ -716,7 +718,8 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
verbose(env, "=");
print_reg_state(env, state, reg);
}
- for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
+ for (i = 0; i < bpf_stack_nr_slots(state); i++) {
+ struct bpf_stack_state *slot = bpf_stack_slot(state, i);
char types_buf[BPF_REG_SIZE + 1];
const char *sep = "";
bool valid = false;
@@ -727,7 +730,7 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
continue;
for (j = 0; j < BPF_REG_SIZE; j++) {
- slot_type = state->stack[i].slot_type[j];
+ slot_type = slot->slot_type[j];
if (slot_type != STACK_INVALID && slot_type != STACK_POISON)
valid = true;
types_buf[j] = slot_type_char[slot_type];
@@ -736,12 +739,12 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
if (!valid)
continue;
- reg = &state->stack[i].spilled_ptr;
- switch (state->stack[i].slot_type[BPF_REG_SIZE - 1]) {
+ reg = &slot->spilled_ptr;
+ switch (slot->slot_type[BPF_REG_SIZE - 1]) {
case STACK_SPILL:
/* print MISC/ZERO/INVALID slots above subreg spill */
for (j = 0; j < BPF_REG_SIZE; j++)
- if (state->stack[i].slot_type[j] == STACK_SPILL)
+ if (slot->slot_type[j] == STACK_SPILL)
break;
types_buf[j] = '\0';
@@ -751,7 +754,7 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
case STACK_DYNPTR:
/* skip to main dynptr slot */
i += BPF_DYNPTR_NR_SLOTS - 1;
- reg = &state->stack[i].spilled_ptr;
+ reg = &bpf_stack_slot(state, i)->spilled_ptr;
verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE);
verbose(env, "=dynptr_%s(", dynptr_type_str(reg->dynptr.type));
@@ -789,6 +792,11 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
verbose(env, " cb");
if (state->in_async_callback_fn)
verbose(env, " async_cb");
+ if (state->loop_stack_cnt) {
+ verbose(env, " loop_stack=");
+ for (i = 0; i < state->loop_stack_cnt; i++)
+ verbose(env, "%s%d", i ? "," : "", state->loop_stack[i].loop_id);
+ }
verbose(env, "\n");
if (!print_all)
mark_verifier_state_clean(env);
diff --git a/kernel/bpf/loops.c b/kernel/bpf/loops.c
new file mode 100644
index 000000000000..2568079c2945
--- /dev/null
+++ b/kernel/bpf/loops.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+#include <linux/bpf_verifier.h>
+
+static bool is_cfg_jump(struct bpf_insn *insn)
+{
+ u8 class = BPF_CLASS(insn->code);
+ u8 opcode = BPF_OP(insn->code);
+
+ switch (class) {
+ case BPF_JMP:
+ case BPF_JMP32:
+ return opcode != BPF_CALL;
+ default:
+ return false;
+ }
+}
+
+static struct bpf_iarray **compute_predecessors(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_iarray *succ, *preds, **result;
+ struct bpf_prog *prog = env->prog;
+ u32 *num_preds, i, s, len = prog->len;
+ struct bpf_insn *insn;
+ size_t sz;
+ void *tmp;
+
+ num_preds = kvcalloc(prog->len, sizeof(u32), GFP_KERNEL_ACCOUNT);
+ if (!num_preds)
+ return NULL;
+
+ /*
+ * 'result' layout:
+ * - array of pointers (struct bpf_iarray *)[len]
+ * - struct bpf_iarray one after another
+ */
+ sz = size_mul(len, sizeof(*result) + sizeof(**result));
+ for (i = 0; i < len; i++) {
+ insn = env->prog->insnsi + i;
+ succ = bpf_insn_successors(env, i);
+ sz = size_add(sz, array_size(succ->cnt, sizeof(*succ->items)));
+ iarray_for_each(s, succ) {
+ num_preds[s]++;
+ }
+ if (bpf_is_ldimm64(insn))
+ i++;
+ }
+
+ result = kvzalloc(sz, GFP_KERNEL_ACCOUNT);
+ if (!result) {
+ kvfree(num_preds);
+ return NULL;
+ }
+
+ tmp = (void *)&result[len];
+ for (i = 0; i < len; i++) {
+ result[i] = tmp;
+ tmp += struct_size(result[i], items, num_preds[i]);
+ }
+
+ for (i = 0; i < len; i++) {
+ insn = env->prog->insnsi + i;
+ succ = bpf_insn_successors(env, i);
+ iarray_for_each(s, succ) {
+ preds = result[s];
+ preds->items[preds->cnt++] = i;
+ }
+ if (succ->cnt > 1 ||
+ (succ->cnt == 1 && num_preds[succ->items[0]] > 1) ||
+ is_cfg_jump(insn))
+ aux[i].bb_end = true;
+ if (bpf_is_ldimm64(insn))
+ i++;
+ }
+
+ kvfree(num_preds);
+ return result;
+}
+
+static int idoms_intersect(struct bpf_verifier_env *env, int a, int b)
+{
+ int *postorder_nums = env->cfg.postorder_nums;
+ int *idoms = env->idoms;
+
+ while (a != b) {
+ while (postorder_nums[a] < postorder_nums[b]) {
+ a = idoms[a];
+ }
+ while (postorder_nums[b] < postorder_nums[a]) {
+ b = idoms[b];
+ }
+ }
+ return a;
+}
+
+/* See "A Simple, Fast Dominance Algorithm" by Cooper et al. for details. */
+static int compute_subprog_idoms(struct bpf_verifier_env *env, struct bpf_iarray **preds,
+ int subprog_idx)
+{
+ struct bpf_subprog_info *subprog = &env->subprog_info[subprog_idx];
+ int start = subprog->start;
+ int po_first = subprog->postorder_start;
+ int po_last = (subprog + 1)->postorder_start - 1;
+ int *idoms = env->idoms;
+ int po_num, pred;
+ u32 work = 0;
+ bool changed;
+
+ idoms[start] = start;
+ changed = true;
+ do {
+ changed = false;
+ /*
+ * Iterate in reverse postorder, skipping the subprog entry:
+ * insn_postorder[po_last] is the subprogram entry.
+ */
+ for (po_num = po_last - 1; po_num >= po_first; po_num--) {
+ int idx = env->cfg.insn_postorder[po_num];
+ int new_idom = -1;
+
+ iarray_for_each(pred, preds[idx]) {
+ if (++work % 1024 == 0) {
+ if (signal_pending(current))
+ return -EAGAIN;
+ cond_resched();
+ }
+
+ if (idoms[pred] == -1)
+ continue;
+ if (new_idom == -1)
+ new_idom = pred;
+ else
+ new_idom = idoms_intersect(env, pred, new_idom);
+ }
+ if (new_idom != -1 && idoms[idx] != new_idom) {
+ idoms[idx] = new_idom;
+ changed = true;
+ }
+ }
+ } while (changed);
+ idoms[start] = -1;
+ return 0;
+}
+
+int bpf_compute_idoms(struct bpf_verifier_env *env)
+{
+ struct bpf_iarray **preds;
+ u32 len = env->prog->len;
+ int *idoms, i, err = 0;
+
+ preds = compute_predecessors(env);
+ if (!preds)
+ return -ENOMEM;
+
+ idoms = kvcalloc(len, sizeof(*idoms), GFP_KERNEL_ACCOUNT);
+ if (!idoms) {
+ kvfree(preds);
+ return -ENOMEM;
+ }
+
+ env->idoms = idoms;
+ for (i = 0; i < len; i++)
+ idoms[i] = -1;
+
+ for (i = 0; i < env->subprog_cnt; i++) {
+ err = compute_subprog_idoms(env, preds, i);
+ if (err)
+ break;
+ }
+
+ kvfree(preds);
+ return err;
+}
+
+struct dfs_state {
+ u32 traversed:1;
+ u32 next_succ:31;
+};
+
+struct loops_dfs {
+ struct dfs_state *state;
+ int *dfs_pos;
+ int *stack;
+};
+
+static void mark_irreducible(struct bpf_verifier_env *env, int h)
+{
+ env->insn_aux_data[h].loop->irreducible = true;
+}
+
+static void add_backedge(struct bpf_verifier_env *env, int from, int h)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_loop *loop = aux[h].loop;
+ int cnt = loop->backedges_cnt;
+
+ if (cnt == MAX_BACKEDGES) {
+ loop->backedges_overflow = true;
+ return;
+ }
+ loop->backedges[cnt].from = from;
+ loop->backedges[cnt].latches_cnt = 0;
+ loop->backedges_cnt++;
+}
+
+static int add_exit(struct bpf_loop *loop, int from, int to)
+{
+ if (loop->exits_overflow)
+ return 0;
+ if (loop->exits_cnt == MAX_LOOP_EXITS) {
+ loop->exits_overflow = true;
+ return 0;
+ }
+ if (!loop->exits) {
+ loop->exits = kvcalloc(MAX_LOOP_EXITS, sizeof(*loop->exits), GFP_KERNEL_ACCOUNT);
+ if (!loop->exits)
+ return -ENOMEM;
+ }
+ loop->exits[loop->exits_cnt] = (struct bpf_loop_exit) {
+ .from = from,
+ .to = to,
+ };
+ loop->exits_cnt++;
+ return 0;
+}
+
+static int mark_as_header(struct bpf_verifier_env *env, int h)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+
+ if (!aux[h].loop) {
+ aux[h].loop = kvzalloc_obj(struct bpf_loop, GFP_KERNEL_ACCOUNT);
+ if (!aux[h].loop)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int assign_header(struct bpf_verifier_env *env, struct loops_dfs *dfs, int n, int h)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ int *dfs_pos = dfs->dfs_pos;
+ int err, nh;
+
+ err = mark_as_header(env, h);
+ if (err)
+ return err;
+
+ /* Don't encode self-loops, otherwise can't reflect loops nesting structure. */
+ if (n == h)
+ return 0;
+
+ /* Make sure that loop headers up the chain are sorted by dfs_pos. */
+ while (aux[n].loop_header != -1) {
+ nh = aux[n].loop_header;
+ if (nh == h)
+ return 0;
+ if (dfs_pos[nh] < dfs_pos[h]) {
+ aux[n].loop_header = h;
+ n = h;
+ h = nh;
+ } else {
+ n = nh;
+ }
+ }
+ aux[n].loop_header = h;
+ return 0;
+}
+
+static bool is_cond_jmp_insn(struct bpf_insn *insn)
+{
+ u8 class = BPF_CLASS(insn->code);
+ u8 opcode = BPF_OP(insn->code);
+
+ if (class != BPF_JMP && class != BPF_JMP32)
+ return false;
+
+ switch (opcode) {
+ case BPF_JEQ:
+ case BPF_JGE:
+ case BPF_JGT:
+ case BPF_JLE:
+ case BPF_JLT:
+ case BPF_JNE:
+ case BPF_JSET:
+ case BPF_JSGE:
+ case BPF_JSGT:
+ case BPF_JSLE:
+ case BPF_JSLT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int bpf_loop_at_index(struct bpf_verifier_env *env, u32 idx)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+
+ return aux[idx].loop ? idx : aux[idx].loop_header;
+}
+
+/*
+ * Collect conditional jumps within n's loop that dominate 'n' and have
+ * a successor outside the loop, nearest first.
+ */
+static void find_dominating_conditions(struct bpf_verifier_env *env, int n,
+ struct bpf_backedge *backedge)
+{
+ struct bpf_insn *insns = env->prog->insnsi;
+ bool t_exits_loop, f_exits_loop;
+ int n_loop, t_loop, f_loop;
+ int *idoms = env->idoms;
+
+ n_loop = bpf_loop_at_index(env, n);
+ if (idoms_intersect(env, n, n_loop) != n_loop)
+ return;
+ while (n >= 0) {
+ if (is_cond_jmp_insn(&insns[n]) && bpf_loop_at_index(env, n) == n_loop) {
+ t_loop = bpf_loop_at_index(env, n + insns[n].off + 1);
+ f_loop = bpf_loop_at_index(env, n + 1);
+ t_exits_loop = t_loop != n_loop && !bpf_is_nested_loop(env, t_loop, n_loop);
+ f_exits_loop = f_loop != n_loop && !bpf_is_nested_loop(env, f_loop, n_loop);
+ if (t_exits_loop || f_exits_loop) {
+ if (backedge->latches_cnt == MAX_LATCHES)
+ return;
+ backedge->latches[backedge->latches_cnt++] = n;
+ }
+ }
+ if (n == n_loop)
+ break;
+ n = idoms[n];
+ }
+}
+
+/*
+ * As described in "A New Algorithm for Identifying Loops in Decompilation" by Wei et al,
+ * adapted to be non-recursive.
+ */
+static int compute_loops_in_subprog(struct bpf_verifier_env *env, struct loops_dfs *dfs,
+ int subprog_idx)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct dfs_state *state = dfs->state;
+ int start = env->subprog_info[subprog_idx].start;
+ int *dfs_pos = dfs->dfs_pos;
+ int *stack = dfs->stack;
+ int s, h, err, cur, stack_sz;
+ struct bpf_iarray *succ;
+ u32 i;
+
+ stack[0] = start;
+ state[start].traversed = true;
+ state[start].next_succ = 0;
+ dfs_pos[start] = 1;
+ stack_sz = 1;
+ i = 0;
+ do {
+ /*
+ * The algorithm should be very fast in practice,
+ * guard against pathological inputs, just in case.
+ */
+ if ((++i % 1024) == 0) {
+ if (signal_pending(current))
+ return -EAGAIN;
+ cond_resched();
+ }
+
+ cur = stack[stack_sz - 1];
+ succ = bpf_insn_successors(env, cur);
+ if (state[cur].next_succ == succ->cnt) {
+ dfs_pos[cur] = 0;
+ stack_sz--;
+ continue;
+ }
+ s = succ->items[state[cur].next_succ];
+ if (!state[s].traversed) {
+ /* Case A: start -> ... -> cur -> s [unexplored] */
+ state[s].traversed = true;
+ state[s].next_succ = 0;
+ stack[stack_sz] = s;
+ dfs_pos[s] = stack_sz + 1;
+ stack_sz++;
+ continue;
+ }
+ /* 's' is fully explored at this point */
+ if (dfs_pos[s]) {
+ /*
+ * start -> ... -> s -> cur --.
+ * ^ |
+ * '----------'
+ * Case B: 's' is in the current DFS path.
+ */
+ err = assign_header(env, dfs, cur, s);
+ if (err)
+ return err;
+ add_backedge(env, cur, s);
+ } else if (aux[s].loop_header == -1) {
+ /*
+ * start -> ... -> ... -> s -> ... -> end
+ * | ^
+ * '---> cur ---'
+ * Case C: 's' is explored, not in the current DFS path,
+ * and not a part of any loop.
+ */
+ } else if (dfs_pos[aux[s].loop_header]) {
+ /*
+ * .----------------------.
+ * v |
+ * start -> ... -> h -> ... -> ... -> s --'
+ * | ^
+ * '---> cur ---'
+ * Case D: 's' is explored, not in current DFS path,
+ * but its innermost loop header is.
+ */
+ err = assign_header(env, dfs, cur, aux[s].loop_header);
+ if (err)
+ return err;
+ } else {
+ /*
+ * case E: 's' is explored, not in current DFS path,
+ * its innermost loop header is not in current DFS path,
+ * hence 's' is another entry into the same loop.
+ */
+ h = aux[s].loop_header;
+ mark_irreducible(env, h);
+ while (aux[h].loop_header != -1) {
+ h = aux[h].loop_header;
+ if (dfs_pos[h]) {
+ err = assign_header(env, dfs, cur, h);
+ if (err)
+ return err;
+ break;
+ }
+ mark_irreducible(env, h);
+ }
+ }
+ state[cur].next_succ++;
+ } while (stack_sz);
+
+ return 0;
+}
+
+bool bpf_is_nested_loop(struct bpf_verifier_env *env, int inner_header, int outer_header)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ int idx;
+
+ for (idx = inner_header; idx >= 0; idx = aux[idx].loop_header)
+ if (aux[idx].loop_header == outer_header)
+ return true;
+
+ return false;
+}
+
+int bpf_compute_loops(struct bpf_verifier_env *env)
+{
+ int i, j, k, s, t, iloop, sloop, err = 0, len = env->prog->len;
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_verifier_log *log = &env->log;
+ struct bpf_backedge *backedge;
+ struct loops_dfs dfs = {};
+ struct bpf_iarray *succ;
+ struct bpf_loop *loop;
+
+ dfs.dfs_pos = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
+ dfs.state = kvcalloc(len, sizeof(struct dfs_state), GFP_KERNEL_ACCOUNT);
+ dfs.stack = kvcalloc(len, sizeof(int), GFP_KERNEL_ACCOUNT);
+ if (!dfs.dfs_pos || !dfs.state || !dfs.stack) {
+ err = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < len; i++)
+ aux[i].loop_header = -1;
+ for (i = 0; i < env->subprog_cnt; i++) {
+ err = compute_loops_in_subprog(env, &dfs, i);
+ if (err)
+ goto out;
+ }
+ /* find latches */
+ for (i = 0; i < len; i++) {
+ loop = aux[i].loop;
+ if (!loop)
+ continue;
+ for (j = 0; j < loop->backedges_cnt; j++) {
+ backedge = &loop->backedges[j];
+ /*
+ * In theory, the backedge->from and it's latch can reside in
+ * an inner loop of `i`, e.g.:
+ *
+ * 1: r7 += 1;
+ * 2: r6 += 1;
+ * if r7 == 2 goto 1b;
+ * if r6 < 2 goto 2b;
+ *
+ * For now, let's assume that latches are unknown for such cases.
+ * (GCC/LLVM handle this by inserting artificial cfg nodes).
+ */
+ if (bpf_loop_at_index(env, backedge->from) != i)
+ continue;
+ find_dominating_conditions(env, backedge->from, backedge);
+ }
+ }
+ /* find exits */
+ for (i = 0; i < len; i++) {
+ iloop = aux[i].loop ? i : aux[i].loop_header;
+ if (iloop < 0)
+ continue;
+ succ = bpf_insn_successors(env, i);
+ iarray_for_each(s, succ) {
+ /*
+ * Nothing left to record once the innermost loop of 'i'
+ * overflowed: the walk below would stop at it right away.
+ */
+ if (aux[iloop].loop->exits_overflow)
+ break;
+ sloop = aux[s].loop ? s : aux[s].loop_header;
+ if (iloop == sloop)
+ continue;
+ if (bpf_is_nested_loop(env, sloop, iloop))
+ continue;
+ /*
+ * At this point 'sloop' is either -1, an outer loop,
+ * or a loop in another branch of the loop hierarchy.
+ */
+ for (t = iloop; t >= 0 && t != sloop; t = aux[t].loop_header) {
+ /*
+ * Account for the following configuration:
+ *
+ * tloop {
+ * iloop {
+ * ... i: goto s;
+ * }
+ * sloop {
+ * s:
+ * ...
+ * }
+ * }
+ */
+ if (bpf_is_nested_loop(env, sloop, t))
+ break;
+ /*
+ * Record edges i -> s as exits from tloop when:
+ *
+ * sloop {
+ * tloop {
+ * iloop {
+ * ... i: goto s;
+ * }
+ * }
+ * s: ...
+ * }
+ */
+ /* Enclosing loops inherit the overflow, see below */
+ if (aux[t].loop->exits_overflow)
+ break;
+ err = add_exit(aux[t].loop, i, s);
+ if (err)
+ goto out;
+ }
+ }
+ if (bpf_is_ldimm64(env->prog->insnsi + i))
+ i++;
+ }
+ /* A nested loop with a truncated exit list can't be abstracted by SCEV */
+ for (i = 0; i < len; i++) {
+ loop = aux[i].loop;
+ if (!loop || !loop->exits_overflow)
+ continue;
+ for (t = aux[i].loop_header; t >= 0; t = aux[t].loop_header)
+ aux[t].loop->exits_overflow = true;
+ }
+
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ for (i = 0; i < len; i++) {
+ loop = aux[i].loop;
+ if (!loop)
+ continue;
+ bpf_log(log, "loop at %d", i);
+ if (aux[i].loop_header >= 0)
+ bpf_log(log, ", nested in %d", aux[i].loop_header);
+ if (loop->irreducible)
+ bpf_log(log, ", irreducible");
+ if (loop->exits_overflow)
+ bpf_log(log, ", too many exits");
+ bpf_log(log, "\n");
+ for (j = 0; j < loop->backedges_cnt; j++) {
+ backedge = &loop->backedges[j];
+ bpf_log(log, " backedge from %d", backedge->from);
+ for (k = 0; k < backedge->latches_cnt; k++) {
+ if (k == 0)
+ bpf_log(log, ", latch at ");
+ if (k == 1)
+ bpf_log(log, ", dominating exits at ");
+ if (k > 1)
+ bpf_log(log, ", ");
+ bpf_log(log, "%d", backedge->latches[k]);
+ }
+ bpf_log(log, "\n");
+ }
+ for (j = 0; j < loop->exits_cnt; j++)
+ bpf_log(log, " exit from %d to %d\n",
+ loop->exits[j].from, loop->exits[j].to);
+ }
+ }
+
+out:
+ kvfree(dfs.dfs_pos);
+ kvfree(dfs.stack);
+ kvfree(dfs.state);
+ return err;
+}
diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
index d2cbab4bdf64..0fa3f2ac0f99 100644
--- a/kernel/bpf/map_in_map.c
+++ b/kernel/bpf/map_in_map.c
@@ -25,6 +25,10 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
if (!inner_map->ops->map_meta_equal)
return ERR_PTR(-ENOTSUPP);
+ /* An inner map has no used_maps reference to hold it under a queued callback. */
+ if (btf_record_has_field(inner_map->record, BPF_RCU_HEAD))
+ return ERR_PTR(-EOPNOTSUPP);
+
inner_map_meta_size = sizeof(*inner_map_meta);
/* In some cases verifier needs to access beyond just base map. */
if (inner_map->ops == &array_map_ops || inner_map->ops == &percpu_array_map_ops)
@@ -106,10 +110,13 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
return ERR_PTR(-ENOTSUPP);
inner_map_meta = map->inner_map_meta;
- if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map))
+ if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map)) {
bpf_map_inc(inner_map);
- else
+ /* Its users are those of the outer maps from now on */
+ WRITE_ONCE(inner_map->was_inner_map, true);
+ } else {
inner_map = ERR_PTR(-EINVAL);
+ }
return inner_map;
}
diff --git a/kernel/bpf/map_iter.c b/kernel/bpf/map_iter.c
index c19b360bad9e..a58c148692e6 100644
--- a/kernel/bpf/map_iter.c
+++ b/kernel/bpf/map_iter.c
@@ -97,13 +97,57 @@ static struct bpf_iter_reg bpf_map_reg_info = {
.seq_info = &bpf_map_seq_info,
};
+/*
+ * For an iterator that hands the live map value to the program as a writable
+ * buffer: the verifier knows nothing of the map, so the special fields of the
+ * value have to lie outside of what the program may write.
+ */
+int bpf_iter_map_check_value_write(const struct bpf_prog *prog,
+ const struct bpf_map *map)
+{
+ const struct btf_record *rec = map->record;
+ const struct btf_field *field;
+ u32 i;
+
+ if (IS_ERR_OR_NULL(rec) || !prog->aux->max_rdwr_write)
+ return 0;
+
+ for (i = 0; i < rec->cnt; i++) {
+ field = &rec->fields[i];
+ if (field->offset < prog->aux->max_rdwr_write &&
+ field->offset + field->size > prog->aux->min_rdwr_write)
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static int bpf_iter_map_check_prog(const struct bpf_prog *prog,
+ const struct bpf_iter_aux_info *aux)
+{
+ const struct bpf_map *map = aux->map;
+ u32 value_size = map->value_size;
+ bool is_percpu;
+
+ is_percpu = map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
+ map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH ||
+ map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
+ if (is_percpu)
+ value_size = round_up(value_size, 8) * num_possible_cpus();
+
+ if (prog->aux->max_rdonly_access > map->key_size ||
+ prog->aux->max_rdwr_access > value_size)
+ return -EACCES;
+
+ /* the program gets a copy of a per-cpu value */
+ return is_percpu ? 0 : bpf_iter_map_check_value_write(prog, map);
+}
+
static int bpf_iter_attach_map(struct bpf_prog *prog,
union bpf_iter_link_info *linfo,
struct bpf_iter_aux_info *aux)
{
- u32 key_acc_size, value_acc_size, key_size, value_size;
struct bpf_map *map;
- bool is_percpu = false;
int err = -EINVAL;
if (!linfo->map.map_fd)
@@ -117,30 +161,26 @@ static int bpf_iter_attach_map(struct bpf_prog *prog,
goto put_map;
}
- if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
- map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH ||
- map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
- is_percpu = true;
- else if (map->map_type != BPF_MAP_TYPE_HASH &&
- map->map_type != BPF_MAP_TYPE_LRU_HASH &&
- map->map_type != BPF_MAP_TYPE_ARRAY &&
- map->map_type != BPF_MAP_TYPE_RHASH)
+ /* The value ctx arg is writable and aliases the live element. */
+ if (btf_record_has_field(map->record, BPF_RCU_HEAD)) {
+ err = -EOPNOTSUPP;
goto put_map;
+ }
- key_acc_size = prog->aux->max_rdonly_access;
- value_acc_size = prog->aux->max_rdwr_access;
- key_size = map->key_size;
- if (!is_percpu)
- value_size = map->value_size;
- else
- value_size = round_up(map->value_size, 8) * num_possible_cpus();
-
- if (key_acc_size > key_size || value_acc_size > value_size) {
- err = -EACCES;
+ if (map->map_type != BPF_MAP_TYPE_PERCPU_HASH &&
+ map->map_type != BPF_MAP_TYPE_LRU_PERCPU_HASH &&
+ map->map_type != BPF_MAP_TYPE_PERCPU_ARRAY &&
+ map->map_type != BPF_MAP_TYPE_HASH &&
+ map->map_type != BPF_MAP_TYPE_LRU_HASH &&
+ map->map_type != BPF_MAP_TYPE_ARRAY &&
+ map->map_type != BPF_MAP_TYPE_RHASH)
goto put_map;
- }
aux->map = map;
+ err = bpf_iter_map_check_prog(prog, aux);
+ if (err)
+ goto put_map;
+
return 0;
put_map:
@@ -173,6 +213,7 @@ static const struct bpf_iter_reg bpf_map_elem_reg_info = {
.target = "bpf_map_elem",
.attach_target = bpf_iter_attach_map,
.detach_target = bpf_iter_detach_map,
+ .check_prog = bpf_iter_map_check_prog,
.show_fdinfo = bpf_iter_map_show_fdinfo,
.fill_link_info = bpf_iter_map_fill_link_info,
.ctx_arg_info_size = 2,
diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c
index d855399812ee..f7bfff1760cc 100644
--- a/kernel/bpf/offload.c
+++ b/kernel/bpf/offload.c
@@ -96,6 +96,22 @@ err_free:
return err;
}
+/*
+ * @prog takes the place of a copy that the caller is about to free. Nothing
+ * keeps the netdev while a program is loaded: once the lock is dropped, no
+ * unregistration is left that has got the old copy from offload->prog.
+ */
+void bpf_prog_dev_bound_moved(struct bpf_prog *prog)
+{
+ if (!bpf_prog_is_dev_bound(prog->aux))
+ return;
+
+ down_read(&bpf_devs_lock);
+ if (prog->aux->offload)
+ prog->aux->offload->prog = prog;
+ up_read(&bpf_devs_lock);
+}
+
static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
{
struct bpf_prog_offload *offload = prog->aux->offload;
@@ -250,7 +266,14 @@ int bpf_prog_dev_bound_init(struct bpf_prog *prog, union bpf_attr *attr)
prog->aux->offload_requested = !(attr->prog_flags & BPF_F_XDP_DEV_BOUND_ONLY);
down_write(&bpf_devs_lock);
- err = __bpf_prog_dev_bound_init(prog, netdev);
+ /*
+ * Behind bpf_dev_bound_netdev_unregister() nobody would take the
+ * program off the device, which goes once the reference is dropped.
+ */
+ if (READ_ONCE(netdev->reg_state) == NETREG_REGISTERED)
+ err = __bpf_prog_dev_bound_init(prog, netdev);
+ else
+ err = -EINVAL;
up_write(&bpf_devs_lock);
out:
diff --git a/kernel/bpf/queue_stack_maps.c b/kernel/bpf/queue_stack_maps.c
index 6e8b18c32a10..1cee2ba6e332 100644
--- a/kernel/bpf/queue_stack_maps.c
+++ b/kernel/bpf/queue_stack_maps.c
@@ -59,6 +59,9 @@ static int queue_stack_map_alloc_check(union bpf_attr *attr)
*/
return -E2BIG;
+ if (attr->max_entries >= U32_MAX / attr->value_size)
+ return -E2BIG;
+
return 0;
}
diff --git a/kernel/bpf/range_tree.c b/kernel/bpf/range_tree.c
index 2f28886f3ff7..2460e78d6a55 100644
--- a/kernel/bpf/range_tree.c
+++ b/kernel/bpf/range_tree.c
@@ -143,16 +143,22 @@ int range_tree_clear(struct range_tree *rt, u32 start, u32 len)
if (rn->rn_start < start && rn->rn_last > last) {
u32 old_last = rn->rn_last;
+ /*
+ * Pre-allocate the right-half node before modifying
+ * the tree. If allocation fails we return -ENOMEM
+ * without altering the range tree.
+ */
+ new_rn = kmalloc_nolock(sizeof(struct range_node),
+ __GFP_ACCOUNT, NUMA_NO_NODE);
+ if (!new_rn)
+ return -ENOMEM;
+
/* Overlaps with the entire clearing range */
range_it_remove(rn, rt);
rn->rn_last = start - 1;
range_it_insert(rn, rt);
- /* Add a range */
- new_rn = kmalloc_nolock(sizeof(struct range_node), __GFP_ACCOUNT,
- NUMA_NO_NODE);
- if (!new_rn)
- return -ENOMEM;
+ /* Add right-half range */
new_rn->rn_start = last + 1;
new_rn->rn_last = old_last;
range_it_insert(new_rn, rt);
@@ -193,6 +199,7 @@ int is_range_tree_set(struct range_tree *rt, u32 start, u32 len)
int range_tree_set(struct range_tree *rt, u32 start, u32 len)
{
u32 last = start + len - 1;
+ struct range_node *new_rn = NULL;
struct range_node *right;
struct range_node *left;
int err;
@@ -202,20 +209,19 @@ int range_tree_set(struct range_tree *rt, u32 start, u32 len)
if (left && left->rn_start <= start && left->rn_last >= last)
return 0;
+ left = range_it_iter_first(rt, start - 1, start - 1);
+ right = range_it_iter_first(rt, last + 1, last + 1);
+ if (!left && !right) {
+ new_rn = kmalloc_nolock(sizeof(struct range_node),
+ __GFP_ACCOUNT, NUMA_NO_NODE);
+ if (!new_rn)
+ return -ENOMEM;
+ }
+
/* Clear out everything in the range we want to set. */
err = range_tree_clear(rt, start, len);
if (err)
- return err;
-
- /* Do we have a left-adjacent range ? */
- left = range_it_iter_first(rt, start - 1, start - 1);
- if (left && left->rn_last + 1 != start)
- return -EFAULT;
-
- /* Do we have a right-adjacent range ? */
- right = range_it_iter_first(rt, last + 1, last + 1);
- if (right && right->rn_start != last + 1)
- return -EFAULT;
+ goto out_free_new;
if (left && right) {
/* Combine left and right adjacent ranges */
@@ -235,14 +241,16 @@ int range_tree_set(struct range_tree *rt, u32 start, u32 len)
right->rn_start = start;
range_it_insert(right, rt);
} else {
- left = kmalloc_nolock(sizeof(struct range_node), __GFP_ACCOUNT, NUMA_NO_NODE);
- if (!left)
- return -ENOMEM;
- left->rn_start = start;
- left->rn_last = last;
- range_it_insert(left, rt);
+ /* No adjacent ranges; use the pre-allocated node */
+ new_rn->rn_start = start;
+ new_rn->rn_last = last;
+ range_it_insert(new_rn, rt);
}
return 0;
+
+out_free_new:
+ kfree_nolock(new_rn);
+ return err;
}
void range_tree_destroy(struct range_tree *rt)
diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c
index 3f1013d80544..447863ece26a 100644
--- a/kernel/bpf/ringbuf.c
+++ b/kernel/bpf/ringbuf.c
@@ -722,7 +722,7 @@ BPF_CALL_2(bpf_ringbuf_submit_dynptr, struct bpf_dynptr_kern *, ptr, u64, flags)
const struct bpf_func_proto bpf_ringbuf_submit_dynptr_proto = {
.func = bpf_ringbuf_submit_dynptr,
.ret_type = RET_VOID,
- .arg1_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | OBJ_RELEASE,
+ .arg1_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | OBJ_RELEASE | MEM_WRITE,
.arg2_type = ARG_ANYTHING,
};
@@ -741,7 +741,7 @@ BPF_CALL_2(bpf_ringbuf_discard_dynptr, struct bpf_dynptr_kern *, ptr, u64, flags
const struct bpf_func_proto bpf_ringbuf_discard_dynptr_proto = {
.func = bpf_ringbuf_discard_dynptr,
.ret_type = RET_VOID,
- .arg1_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | OBJ_RELEASE,
+ .arg1_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | OBJ_RELEASE | MEM_WRITE,
.arg2_type = ARG_ANYTHING,
};
diff --git a/kernel/bpf/scev.c b/kernel/bpf/scev.c
new file mode 100644
index 000000000000..fea77772ca96
--- /dev/null
+++ b/kernel/bpf/scev.c
@@ -0,0 +1,2916 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+
+#include "linux/cnum.h"
+#include <linux/bpf_verifier.h>
+#include <linux/jhash.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/min_heap.h>
+#include <linux/bug.h>
+#include <linux/tnum.h>
+#include <linux/overflow.h>
+
+#define REGS_NUM BPF_SCEV_REGS_NUM
+#define UNKNOWN_EXPR_ID 0
+#define OPAQUE_EXPR_ID 1
+
+/*
+ * BPF instructions use 'code', 'src_reg', 'off' and 'imm' fields for instruction encoding.
+ * For scalar evolution purpose we want to reuse most of the opcode definitions,
+ * but also add a few custom operations (REG and IMM).
+ * Use the enum below to uniformly represent all operations relevant for SCEV.
+ */
+enum expr_op {
+ /* leave range 0..255 for standard bpf opcodes */
+ UNKNOWN = 256, /* start custom opcodes from the second byte */
+ REG,
+ IMM,
+ SDIV, SMOD,
+ SEXT8, SEXT16, SEXT32,
+ ZEXT8, ZEXT16, ZEXT32,
+ BSWAP16, BSWAP32, BSWAP64,
+ /* during the loop body execution the value can be either of param[0] or param[1] */
+ ANY,
+ /* some value that verifier is not going to track precisely */
+ OPAQUE,
+ /* '*(u8/16/32 *)(r10 + X) = Y' writes define slot contents only partially */
+ SPILL8, SPILL16, SPILL32,
+ /*
+ * SCEV expression corresponding to linear equation 'param[0] + param[1] * k',
+ * where k is a loop iteration number. Loop here refers to innermost loop
+ * containing instruction associated with this expression, as returned by
+ * bpf_loop_at_index().
+ */
+ LINEAR_SCEV,
+};
+
+struct expr {
+ u32 op;
+ union {
+ u32 params[2];
+ s64 imm;
+ };
+};
+
+struct expr_bucket {
+ u32 cnt;
+ u32 cap;
+ u32 ids[];
+};
+
+struct env {
+ bool empty;
+ u32 reg2expr[REGS_NUM];
+ u32 reg2scev[REGS_NUM];
+};
+
+struct insn_envs {
+ u32 cnt;
+ struct {
+ int loop_header;
+ struct env *env;
+ } entries[];
+};
+
+#define EXPR_STACK_DEPTH 8
+
+struct expr_stack_elt {
+ u32 id:28;
+ u32 pre:1;
+ u32 next_param:2;
+};
+
+DEFINE_MIN_HEAP(int, scev_worklist);
+
+struct scev {
+ /* Read-only representatives for stack slots without a saved register. */
+ struct bpf_reg_state zero_reg;
+ struct bpf_reg_state unknown_reg;
+ /*
+ * Expressions are identified by id, exprs_ht ensures that
+ * each expression exists as a unique instance.
+ * This allows for fast equivalence check: id1 === id2.
+ */
+ struct expr_bucket **exprs_ht;
+ struct scev_worklist worklist;
+ struct insn_envs **envs;
+ struct expr *exprs;
+ /*
+ * Loops are analyzed one by one, this array keeps track if a particular
+ * instruction was visited on a current pass.
+ */
+ u32 *discovered;
+ u32 exprs_ht_cnt;
+ int exprs_cnt;
+ int exprs_cap;
+ int envs_cnt;
+ int stack_sz;
+ struct expr_stack_elt expr_stack[EXPR_STACK_DEPTH];
+ u32 ids_buf[EXPR_STACK_DEPTH];
+};
+
+static u32 expr_hash(struct expr *e)
+{
+ return jhash_3words(e->op, e->params[0], e->params[1], 0);
+}
+
+static int expr_eq(struct expr *a, struct expr *b)
+{
+ return a->op == b->op && a->imm == b->imm;
+}
+
+static int add_expr(struct scev *scev, struct expr e)
+{
+ struct expr_bucket *bucket;
+ u32 i, id, hash, new_cap;
+ void *tmp;
+
+ hash = expr_hash(&e) & (scev->exprs_ht_cnt - 1);
+ bucket = scev->exprs_ht[hash];
+
+ if (bucket) {
+ for (i = 0; i < bucket->cnt; i++) {
+ id = bucket->ids[i];
+ if (expr_eq(&e, &scev->exprs[id]))
+ return id;
+ }
+ }
+
+ if (!bucket || bucket->cap == bucket->cnt) {
+ new_cap = bucket ? bucket->cap * 2 : 4;
+ bucket = kvrealloc(bucket, sizeof(*bucket) + sizeof(u32) * new_cap, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!bucket)
+ return -ENOMEM;
+ scev->exprs_ht[hash] = bucket;
+ bucket->cap = new_cap;
+ }
+
+ if (scev->exprs_cnt == scev->exprs_cap) {
+ new_cap = scev->exprs_cap ? scev->exprs_cap * 2 : 256;
+ tmp = kvrealloc(scev->exprs, sizeof(struct expr) * new_cap, GFP_KERNEL_ACCOUNT);
+ if (!tmp)
+ return -ENOMEM;
+ scev->exprs = tmp;
+ scev->exprs_cap = new_cap;
+ }
+
+ id = scev->exprs_cnt++;
+ scev->exprs[id] = e;
+ bucket->ids[bucket->cnt++] = id;
+ return id;
+}
+
+static int expr2(struct scev *scev, u32 op, int a, int b)
+{
+ if (a < 0)
+ return a;
+ if (b < 0)
+ return b;
+ return add_expr(scev, (struct expr){ .op = op, .params = {a, b} });
+}
+
+static int expr1(struct scev *scev, u32 op, int a)
+{
+ if (a < 0)
+ return a;
+ return expr2(scev, op, a, 0);
+}
+
+static int expr0(struct scev *scev, u32 op)
+{
+ return expr2(scev, op, 0, 0);
+}
+
+static int imm_expr(struct scev *scev, s64 value)
+{
+ return add_expr(scev, (struct expr){ .op = IMM, .imm = value });
+}
+
+static bool same_exprs(struct scev *scev, int id_a, int id_b)
+{
+ return id_a == id_b;
+}
+
+static bool is_imm(struct scev *scev, int id, s64 *imm)
+{
+ if (scev->exprs[id].op != IMM)
+ return false;
+ *imm = scev->exprs[id].imm;
+ return true;
+}
+
+static bool is_op(struct scev *scev, int id, u32 op)
+{
+ if (scev->exprs[id].op != op)
+ return false;
+ return true;
+}
+
+static bool is_unop(struct scev *scev, enum expr_op op, int id, u32 *p0)
+{
+ if (scev->exprs[id].op != op)
+ return false;
+ *p0 = scev->exprs[id].params[0];
+ return true;
+}
+
+static bool is_binop(struct scev *scev, enum expr_op op, int id, u32 *p0, u32 *p1)
+{
+ if (scev->exprs[id].op != op)
+ return false;
+ *p0 = scev->exprs[id].params[0];
+ *p1 = scev->exprs[id].params[1];
+ return true;
+}
+
+static bool is_reg(struct scev *scev, int id, u32 *reg)
+{
+ return is_unop(scev, REG, id, reg);
+}
+
+static bool is_add(struct scev *scev, int id, u32 *left, u32 *right)
+{
+ return is_binop(scev, BPF_ADD, id, left, right);
+}
+
+static bool is_zext32(struct scev *scev, int id, u32 *left)
+{
+ return is_unop(scev, ZEXT32, id, left);
+}
+
+static bool is_any(struct scev *scev, int id, u32 *left, u32 *right)
+{
+ return is_binop(scev, ANY, id, left, right);
+}
+
+static bool is_linear(struct scev *scev, int id, u32 *base, u32 *slope)
+{
+ return is_binop(scev, LINEAR_SCEV, id, base, slope);
+}
+
+static bool is_linear_or_linear32(struct scev *scev, int id, u32 *base, u32 *slope, bool *zext32)
+{
+ u32 t;
+
+ *zext32 = is_zext32(scev, id, &t);
+ return is_linear(scev, *zext32 ? t : id, base, slope);
+}
+
+static bool is_opaque(struct scev *scev, int id)
+{
+ return is_op(scev, id, OPAQUE);
+}
+
+static void log_reg(struct bpf_verifier_env *env, u32 reg)
+{
+ if (reg < MAX_BPF_REG)
+ bpf_log(&env->log, "r%d", reg);
+ else
+ bpf_log(&env->log, "*fp%d", (MAX_BPF_REG - reg - 1) * 8);
+}
+
+static const char *op_str(u32 op)
+{
+ switch (op) {
+ case BPF_ADD: return "+";
+ case BPF_SUB: return "-";
+ case BPF_MUL: return "*";
+ case BPF_DIV: return "/";
+ case SDIV: return "s/";
+ case BPF_OR: return "|";
+ case BPF_AND: return "&";
+ case BPF_LSH: return "<<";
+ case BPF_RSH: return ">>";
+ case BPF_NEG: return "-";
+ case BPF_MOD: return "%";
+ case SMOD: return "s%";
+ case BPF_XOR: return "^";
+ case BPF_ARSH: return "s>>";
+ case SEXT8: return "sext8";
+ case SEXT16: return "sext16";
+ case SEXT32: return "sext32";
+ case ZEXT8: return "zext8";
+ case ZEXT16: return "zext16";
+ case ZEXT32: return "zext32";
+ case BSWAP16: return "bswap16";
+ case BSWAP32: return "bswap32";
+ case BSWAP64: return "bswap64";
+ case SPILL8: return "spill8";
+ case SPILL16: return "spill16";
+ case SPILL32: return "spill32";
+ case ANY: return "any";
+ case LINEAR_SCEV: return "linear";
+ }
+ return NULL;
+}
+
+static u32 op_params_num(u32 op)
+{
+ switch (op) {
+ case BPF_ADD:
+ case BPF_SUB:
+ case BPF_MUL:
+ case BPF_DIV:
+ case BPF_MOD:
+ case BPF_OR:
+ case BPF_XOR:
+ case BPF_AND:
+ case BPF_LSH:
+ case BPF_RSH:
+ case BPF_ARSH:
+ case SDIV:
+ case SMOD:
+ case ANY:
+ case LINEAR_SCEV:
+ return 2;
+ case BPF_NEG:
+ case SEXT8:
+ case SEXT16:
+ case SEXT32:
+ case ZEXT8:
+ case ZEXT16:
+ case ZEXT32:
+ case BSWAP16:
+ case BSWAP32:
+ case BSWAP64:
+ case SPILL8:
+ case SPILL16:
+ case SPILL32:
+ return 1;
+ case REG:
+ case IMM:
+ case OPAQUE:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static bool expr_stack_push(struct scev *scev, u32 id)
+{
+ if (scev->stack_sz >= EXPR_STACK_DEPTH)
+ return false;
+ scev->expr_stack[scev->stack_sz].id = id;
+ scev->expr_stack[scev->stack_sz].pre = true;
+ scev->expr_stack[scev->stack_sz].next_param = 0;
+ scev->stack_sz++;
+ return true;
+}
+
+enum {
+ PRE = BIT(1), POST = BIT(2), DEPTH_LIMIT = BIT(3)
+};
+
+static bool expr_next(struct scev *scev, u32 *id, u32 *order)
+{
+ struct expr_stack_elt *elt;
+ struct expr *expr;
+ u32 num_params;
+
+ if (scev->stack_sz == 0)
+ return false;
+
+ elt = &scev->expr_stack[scev->stack_sz - 1];
+ *id = elt->id;
+ *order = 0;
+ expr = &scev->exprs[elt->id];
+ num_params = op_params_num(expr->op);
+ if (elt->pre) {
+ elt->pre = false;
+ *order = PRE;
+ return true;
+ }
+ if (elt->next_param == num_params) {
+ *order = POST;
+ scev->stack_sz--;
+ return true;
+ }
+ if (scev->stack_sz == EXPR_STACK_DEPTH) {
+ *order = POST | DEPTH_LIMIT;
+ scev->stack_sz--;
+ return true;
+ }
+ expr_stack_push(scev, expr->params[elt->next_param]);
+ elt->next_param++;
+ return expr_next(scev, id, order);
+}
+
+static void log_expr(struct bpf_verifier_env *env, u32 id)
+{
+ struct bpf_verifier_log *log = &env->log;
+ struct scev *scev = env->scev;
+ struct expr *expr;
+ const char *str;
+ u32 order;
+
+ scev->stack_sz = 0;
+ expr_stack_push(scev, id);
+ while (expr_next(scev, &id, &order)) {
+ if ((order & PRE) && scev->stack_sz > 1)
+ bpf_log(log, " ");
+ expr = &scev->exprs[id];
+ switch (expr->op) {
+ case UNKNOWN:
+ if (order & PRE)
+ bpf_log(log, "?");
+ break;
+ case OPAQUE:
+ if (order & PRE)
+ bpf_log(log, "_");
+ break;
+ case REG:
+ if (order & PRE)
+ log_reg(env, expr->params[0]);
+ break;
+ case IMM:
+ if (order & PRE)
+ bpf_log(log, "%lld", expr->imm);
+ break;
+ default:
+ if (order & PRE) {
+ str = op_str(expr->op);
+ bpf_log(log, "(");
+ if (str)
+ bpf_log(log, "%s", str);
+ else
+ bpf_log(log, "bad-expr-op %x", expr->op);
+ }
+ if (order & DEPTH_LIMIT)
+ bpf_log(log, "...");
+ if (order & POST)
+ bpf_log(log, ")");
+ }
+ }
+}
+
+static bool reg_alive_at(struct bpf_verifier_env *env, u32 reg, u32 insn_idx)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ u16 live_regs = aux[insn_idx].live_regs_before;
+
+ return reg < MAX_BPF_REG
+ ? (BIT(reg) & live_regs)
+ : test_bit(reg - MAX_BPF_REG, aux[insn_idx].live_stack_before);
+}
+
+static void print_env(struct bpf_verifier_env *env, struct env *e, u32 insn_idx)
+{
+ struct bpf_verifier_log *log = &env->log;
+ struct scev *scev = env->scev;
+ bool printed_some = false;
+ bool is_self_scev;
+ bool is_self_reg;
+ int i, r;
+
+ if (e->empty) {
+ bpf_log(log, " <empty>\n");
+ return;
+ }
+
+ for (i = 0; i < REGS_NUM; i++) {
+ if (!reg_alive_at(env, i, insn_idx))
+ continue;
+ is_self_reg = is_reg(scev, e->reg2expr[i], &r) && i == r;
+ is_self_scev = is_reg(scev, e->reg2scev[i], &r) && i == r;
+ if (is_self_reg && is_self_scev)
+ continue;
+ printed_some = true;
+ bpf_log(log, " ");
+ log_reg(env, i);
+ bpf_log(log, "=");
+ log_expr(env, e->reg2expr[i]);
+ if (e->reg2expr[i] != UNKNOWN_EXPR_ID) {
+ bpf_log(log, " / ");
+ log_expr(env, e->reg2scev[i]);
+ }
+ bpf_log(log, "\n");
+ }
+ if (!printed_some)
+ bpf_log(log, " <all regs unchanged>\n");
+}
+static struct env *find_loop_env(struct scev *scev, int loop_header, int insn_idx)
+{
+ struct insn_envs *envs = scev->envs[insn_idx];
+ u32 i;
+
+ if (!envs)
+ return NULL;
+ for (i = 0; i < envs->cnt; i++)
+ if (envs->entries[i].loop_header == loop_header)
+ return envs->entries[i].env;
+ return NULL;
+}
+
+static struct env *find_header_env(struct scev *scev, int header)
+{
+ return find_loop_env(scev, header, header);
+}
+
+static struct env *get_loop_env(struct scev *scev, int loop_header, int insn_idx)
+{
+ struct insn_envs *envs = scev->envs[insn_idx];
+ u32 cnt = envs ? envs->cnt : 0;
+ struct insn_envs *tmp;
+ struct env *e;
+
+ e = find_loop_env(scev, loop_header, insn_idx);
+ if (e)
+ return e;
+
+ e = kzalloc(sizeof(*e), GFP_KERNEL_ACCOUNT);
+ if (!e)
+ return NULL;
+
+ tmp = krealloc(envs, struct_size(envs, entries, cnt + 1), GFP_KERNEL_ACCOUNT);
+ if (!tmp) {
+ kfree(e);
+ return NULL;
+ }
+
+ e->empty = true;
+ tmp->entries[cnt].loop_header = loop_header;
+ tmp->entries[cnt].env = e;
+ tmp->cnt = cnt + 1;
+ scev->envs[insn_idx] = tmp;
+ return e;
+}
+
+static int replace_reg(struct scev *scev, struct env *e, u32 reg, int id)
+{
+ if (id < 0)
+ return id;
+ e->reg2expr[reg] = id;
+ return 0;
+}
+
+static int setup_initial_loop_env(struct bpf_verifier_env *env, struct env *e, int insn_idx)
+{
+ struct scev *scev = env->scev;
+ int i, err;
+
+ /*
+ * Initial assignments are of form rX -> rX are needed as seeds for subsequent
+ * work queue pop / transfer() calls. Because e->empty is true, the first backedge
+ * join() will overwrite the them. compute_header_scevs() accounts for register
+ * states at loop entry.
+ */
+ for (i = 0; i < REGS_NUM; i++) {
+ err = replace_reg(scev, e, i, expr1(scev, REG, i));
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static void forget_call_regs(struct env *e)
+{
+ int i;
+
+ for (i = BPF_REG_0; i <= BPF_REG_5; i++)
+ e->reg2expr[i] = UNKNOWN_EXPR_ID;
+}
+
+static u32 spill_spi(struct bpf_insn *insn)
+{
+ return -insn->off / BPF_REG_SIZE - 1;
+}
+
+static u32 off_to_reg(struct bpf_insn *insn)
+{
+ return spill_spi(insn) + MAX_BPF_REG;
+}
+
+static bool is_spill_off(int off)
+{
+ return off % BPF_REG_SIZE == 0 &&
+ off <= -BPF_REG_SIZE &&
+ off >= -MAX_BPF_STACK_JIT;
+}
+
+static void mark_opaque(struct env *e, int off, int size)
+{
+ int b, spi;
+
+ for (b = off; b < off + size; b++) {
+ if (b >= 0 || b < -MAX_BPF_STACK_JIT)
+ continue;
+ spi = (-b - 1) / BPF_REG_SIZE;
+ e->reg2expr[MAX_BPF_REG + spi] = OPAQUE_EXPR_ID;
+ }
+}
+
+static int mk_spill(struct scev *scev, u8 size, int id)
+{
+ switch (size) {
+ case BPF_B: return expr1(scev, SPILL8, id);
+ case BPF_H: return expr1(scev, SPILL16, id);
+ case BPF_W: return expr1(scev, SPILL32, id);
+ case BPF_DW: return id;
+ }
+ return UNKNOWN_EXPR_ID;
+}
+
+static int mk_fill(struct scev *scev, int id, u8 code)
+{
+ bool sx = BPF_MODE(code) == BPF_MEMSX;
+
+#ifdef __BIG_ENDIAN
+ /* A narrow fill from a DW spill reads high bits on big-endian. */
+ if (BPF_SIZE(code) != BPF_DW)
+ return OPAQUE_EXPR_ID;
+#endif
+
+ switch (BPF_SIZE(code)) {
+ case BPF_B: return expr1(scev, sx ? SEXT8 : ZEXT8, id);
+ case BPF_H: return expr1(scev, sx ? SEXT16 : ZEXT16, id);
+ case BPF_W: return expr1(scev, sx ? SEXT32 : ZEXT32, id);
+ case BPF_DW: return sx ? UNKNOWN_EXPR_ID : id; /* DW sign-extended load is invalid */
+ }
+ return UNKNOWN_EXPR_ID;
+}
+
+static int maybe_store_fp(struct scev *scev, struct env *e, struct bpf_insn *insn, int id)
+{
+ u8 size = BPF_SIZE(insn->code);
+
+ if (insn->dst_reg != BPF_REG_FP)
+ return 0;
+ if (is_spill_off(insn->off))
+ return replace_reg(scev, e, off_to_reg(insn), mk_spill(scev, size, id));
+ mark_opaque(e, insn->off, bpf_size_to_bytes(size));
+ return 0;
+}
+
+static int maybe_load_fp(struct scev *scev, struct env *e, struct bpf_insn *insn)
+{
+ int id;
+
+ if (insn->src_reg == BPF_REG_FP && is_spill_off(insn->off))
+ id = mk_fill(scev, e->reg2expr[off_to_reg(insn)], insn->code);
+ else
+ id = OPAQUE_EXPR_ID;
+ return replace_reg(scev, e, insn->dst_reg, id);
+}
+
+static int transfer(struct bpf_verifier_env *env, struct env *e, int idx)
+{
+ const bool little_endian = htons(0x3412) == 0x1234;
+ struct bpf_insn *insn = &env->prog->insnsi[idx];
+ struct scev *scev = env->scev;
+ u32 *reg2expr = e->reg2expr;
+ u8 class = BPF_CLASS(insn->code);
+ u8 x_or_k = BPF_SRC(insn->code);
+ u8 opcode = BPF_OP(insn->code);
+ u8 mode = BPF_MODE(insn->code);
+ u32 dst = insn->dst_reg;
+ u32 src = insn->src_reg;
+ u32 op, sext;
+ bool need_bswap;
+ int i, id;
+ s64 imm;
+
+ switch (class) {
+ case BPF_ALU:
+ case BPF_ALU64:
+ switch (opcode) {
+ case BPF_MOV:
+ switch (insn->off) {
+ case 0: sext = 0; break;
+ case 8: sext = SEXT8; break;
+ case 16: sext = SEXT16; break;
+ case 32: sext = SEXT32; break;
+ default:
+ goto mark_dst_unknown;
+ }
+
+ if (x_or_k == BPF_X && insn->imm == 0)
+ id = reg2expr[src];
+ else if (x_or_k == BPF_K && src == 0 && insn->off == 0)
+ id = imm_expr(scev, insn->imm);
+ else
+ goto mark_dst_unknown;
+
+ if (sext)
+ id = expr1(scev, sext, id);
+ if (class == BPF_ALU)
+ id = expr1(scev, ZEXT32, id);
+
+ return replace_reg(scev, e, dst, id);
+
+ case BPF_ADD:
+ case BPF_SUB:
+ case BPF_MUL:
+ case BPF_DIV:
+ case BPF_MOD:
+ case BPF_OR:
+ case BPF_XOR:
+ case BPF_AND:
+ case BPF_LSH:
+ case BPF_RSH:
+ case BPF_ARSH:
+ if (opcode == BPF_DIV && insn->off == 1)
+ op = SDIV;
+ else if (opcode == BPF_MOD && insn->off == 1)
+ op = SMOD;
+ else if (insn->off == 0)
+ op = opcode;
+ else
+ goto mark_dst_unknown;
+
+ if (x_or_k == BPF_X && insn->imm == 0)
+ id = reg2expr[src];
+ else if (x_or_k == BPF_K && src == 0)
+ id = imm_expr(scev, insn->imm);
+ else
+ goto mark_dst_unknown;
+
+ id = expr2(scev, op, reg2expr[dst], id);
+
+ if (class == BPF_ALU)
+ id = expr1(scev, ZEXT32, id);
+
+ return replace_reg(scev, e, dst, id);
+
+ case BPF_NEG:
+ if (src == 0 && insn->off == 0 && insn->imm == 0)
+ id = expr1(scev, BPF_NEG, reg2expr[dst]);
+ else
+ goto mark_dst_unknown;
+
+ if (class == BPF_ALU)
+ id = expr1(scev, ZEXT32, id);
+
+ return replace_reg(scev, e, dst, id);
+
+ case BPF_END:
+ if (src != 0 || insn->off != 0 || (class == BPF_ALU64 && x_or_k != 0))
+ goto mark_dst_unknown;
+
+ need_bswap = class == BPF_ALU64 || ((x_or_k == BPF_TO_LE) != little_endian);
+ switch (insn->imm) {
+ case 16: op = need_bswap ? BSWAP16 : ZEXT16; break;
+ case 32: op = need_bswap ? BSWAP32 : ZEXT32; break;
+ case 64: op = need_bswap ? BSWAP64 : 0; break;
+ default:
+ goto mark_dst_unknown;
+ }
+
+ id = reg2expr[dst];
+ if (op)
+ id = expr1(scev, op, reg2expr[dst]);
+
+ return replace_reg(scev, e, dst, id);
+ default:
+ goto mark_dst_unknown;
+ }
+ break;
+ case BPF_LDX:
+ switch (mode) {
+ case BPF_MEM:
+ case BPF_MEMSX:
+ return maybe_load_fp(scev, e, insn);
+ default:
+ goto mark_dst_unknown;
+ }
+ case BPF_STX:
+ switch (mode) {
+ case BPF_MEM:
+ return maybe_store_fp(scev, e, insn, reg2expr[src]);
+ case BPF_ATOMIC:
+ if (insn->imm == BPF_LOAD_ACQ)
+ return maybe_load_fp(scev, e, insn);
+ if (insn->imm == BPF_STORE_REL) {
+ return maybe_store_fp(scev, e, insn, reg2expr[src]);
+ }
+ /*
+ * verifier does not track other atomic ops precisely,
+ * hence mark the results as opaque.
+ */
+ if (insn->dst_reg == BPF_REG_FP)
+ mark_opaque(e, insn->off, bpf_size_to_bytes(BPF_SIZE(insn->code)));
+ if (insn->imm == BPF_CMPXCHG)
+ return replace_reg(scev, e, BPF_REG_0, OPAQUE_EXPR_ID);
+ if (insn->imm & BPF_FETCH)
+ return replace_reg(scev, e, src, OPAQUE_EXPR_ID);
+ break;
+ }
+ break;
+ case BPF_ST:
+ if (insn->dst_reg == BPF_REG_FP) {
+ id = imm_expr(scev, insn->imm);
+ if (id < 0)
+ return id;
+ return maybe_store_fp(scev, e, insn, id);
+ }
+ break;
+ case BPF_JMP:
+ case BPF_JMP32:
+ if (opcode == BPF_CALL)
+ forget_call_regs(e);
+ /* for non-CALL there are no changes in register states */
+ break;
+ case BPF_LD:
+ switch (mode) {
+ case BPF_IMM:
+ /* rX = imm ll */
+ if (BPF_SIZE(insn->code) == BPF_DW && insn->src_reg == 0) {
+ imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
+ return replace_reg(scev, e, dst, imm_expr(scev, imm));
+ }
+ /* map, map value, BTF id, function */
+ if (BPF_SIZE(insn->code) == BPF_DW)
+ return replace_reg(scev, e, dst, OPAQUE_EXPR_ID);
+ goto mark_dst_unknown;
+ case BPF_ABS:
+ case BPF_IND:
+ forget_call_regs(e);
+ break;
+ default:
+ goto mark_dst_unknown;
+ }
+ break;
+ default:
+ /* unknown instruction, nuke state */
+ for (i = 0; i < REGS_NUM; i++)
+ reg2expr[i] = UNKNOWN_EXPR_ID;
+ break;
+ }
+ return 0;
+
+mark_dst_unknown:
+ reg2expr[dst] = UNKNOWN_EXPR_ID;
+ return 0;
+}
+
+/*
+ * Construct minimal 'ANY' expression by traversing 'a' and 'b'
+ * and accumulating non-duplicated non-ANY entries.
+ */
+static int mk_any(struct scev *scev, int a, int b)
+{
+ struct expr_stack_elt *elt;
+ struct expr *expr;
+ u32 i, j, ids_buf_sz;
+ u32 roots[2] = {a, b};
+ int id;
+
+ if (a < 0)
+ return a;
+ if (b < 0)
+ return b;
+ ids_buf_sz = 0;
+ for (i = 0; i < ARRAY_SIZE(roots); i++) {
+ scev->stack_sz = 0;
+ expr_stack_push(scev, roots[i]);
+ while (scev->stack_sz) {
+ elt = &scev->expr_stack[--scev->stack_sz];
+ id = elt->id;
+ expr = &scev->exprs[elt->id];
+ if (expr->op == ANY) {
+ if (!expr_stack_push(scev, expr->params[1]) ||
+ !expr_stack_push(scev, expr->params[0]))
+ return UNKNOWN_EXPR_ID;
+ } else {
+ for (j = 0; j < ids_buf_sz; j++) {
+ if (same_exprs(scev, scev->ids_buf[j], id))
+ goto next;
+ }
+ if (ids_buf_sz == ARRAY_SIZE(scev->ids_buf))
+ return UNKNOWN_EXPR_ID;
+ scev->ids_buf[ids_buf_sz++] = id;
+ }
+next:;
+ }
+ }
+ if (WARN_ON(ids_buf_sz == 0))
+ return -EFAULT;
+ id = scev->ids_buf[0];
+ for (i = 1; i < ids_buf_sz; i++) {
+ id = expr2(scev, ANY, id, scev->ids_buf[i]);
+ if (id < 0)
+ return id;
+ }
+ return id;
+}
+
+static int join(struct scev *scev, struct env *acc, struct env *cur)
+{
+ int i, id;
+
+ if (acc->empty) {
+ memcpy(acc, cur, sizeof(*acc));
+ acc->empty = false;
+ return 0;
+ }
+
+ for (i = 0; i < REGS_NUM; i++) {
+ if (!same_exprs(scev, acc->reg2expr[i], cur->reg2expr[i])) {
+ id = mk_any(scev, acc->reg2expr[i], cur->reg2expr[i]);
+ if (id < 0)
+ return id;
+ acc->reg2expr[i] = id;
+ }
+ }
+ return 0;
+}
+
+/* Mark any register modified in 'e' as unknown in 'acc'. */
+static void forget_modified_regs(struct scev *scev, struct env *acc, struct env *e)
+{
+ u32 i, reg;
+
+ for (i = 0; i < REGS_NUM; i++) {
+ if (e && is_reg(scev, e->reg2expr[i], &reg) && reg == i)
+ continue;
+ acc->reg2expr[i] = UNKNOWN_EXPR_ID;
+ }
+}
+
+/* For a loop H at `header`, mark any register modified as a result of H execution as unknown. */
+static void forget_non_invariants(struct bpf_verifier_env *env, struct env *acc, int header)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_loop *loop = aux[header].loop;
+ struct scev *scev = env->scev;
+ int i, idx, h;
+
+ acc->empty = false;
+ forget_modified_regs(scev, acc, find_header_env(scev, header));
+ for (i = 0; i < loop->exits_cnt; i++) {
+ idx = loop->exits[i].from;
+ h = bpf_loop_at_index(env, idx);
+ /*
+ * In order to account for situations like this:
+ *
+ * header:
+ * do {
+ * saved = rX;
+ * rX = -1000;
+ * do { // H
+ * if (random())
+ * goto out; // E
+ * } while (inner_again);
+ * rX = saved;
+ * } while (outer_again);
+ * out:
+ *
+ * For exits originating in nested loops check that rX is invariant
+ * both at the header (H) and at exit (E).
+ */
+ while (h != header) {
+ forget_modified_regs(scev, acc, find_header_env(scev, h));
+ forget_modified_regs(scev, acc, find_loop_env(scev, h, idx));
+ idx = h;
+ h = aux[h].loop_header;
+ }
+ /*
+ * For exits originating at the same loop the header is already accounted for,
+ * check only the exit environment.
+ */
+ forget_modified_regs(scev, acc, find_loop_env(scev, header, idx));
+ }
+}
+
+/* Order worklist in reverse post-order. */
+static bool reverse_postorder_less(const void *a, const void *b, void *arg)
+{
+ const int *rank = arg;
+
+ return rank[*(const int *)a] > rank[*(const int *)b];
+}
+
+static const struct min_heap_callbacks worklist_cb = {
+ .less = reverse_postorder_less,
+};
+
+static int worklist_push(struct bpf_verifier_env *env, int loop_header, int idx)
+{
+ struct scev *scev = env->scev;
+ struct scev_worklist *worklist = &scev->worklist;
+ u32 tag = (u32)loop_header + 1;
+
+ if (scev->discovered[idx] == tag)
+ return 0;
+
+ if (min_heap_full_inline(worklist)) {
+ size_t size = worklist->size ? worklist->size * 2 : 16;
+ int *data;
+
+ data = kvrealloc(worklist->data, array_size(size, sizeof(*data)),
+ GFP_KERNEL_ACCOUNT);
+ if (!data)
+ return -ENOMEM;
+ worklist->data = data;
+ worklist->size = size;
+ }
+ if (!min_heap_push_inline(worklist, &idx, &worklist_cb, env->cfg.postorder_nums))
+ return -EFAULT;
+
+ scev->discovered[idx] = tag;
+ return 0;
+}
+
+static bool reg_alive_at_succ(struct bpf_verifier_env *env, u32 r, u32 insn_idx)
+{
+ struct bpf_iarray *succ = bpf_insn_successors(env, insn_idx);
+ u32 i;
+
+ for (i = 0; i < succ->cnt; i++)
+ if (reg_alive_at(env, r, succ->items[i]))
+ return true;
+ return false;
+}
+
+enum changes_at {
+ LOG_AT_TRANSFER,
+ LOG_AT_JOIN,
+};
+
+static void log_env_changes(struct bpf_verifier_env *env, enum changes_at at,
+ struct env *old, struct env *new, int insn_idx)
+{
+ struct bpf_verifier_log *log = &env->log;
+ struct scev *scev = env->scev;
+ u64 null_pos = log->end_pos;
+ u32 old_id, new_id, regno;
+ bool any_changes = false;
+ bool is_self_scev;
+ u64 len;
+ int r;
+
+ bpf_log(log, "%s %4d: ", at == LOG_AT_TRANSFER ? "t" : "j", insn_idx);
+ bpf_verbose_insn(env, &env->prog->insnsi[insn_idx]);
+ len = log->end_pos - null_pos;
+ bpf_log(log, "%*s", max(37 - (int)len, 1), " ");
+ bpf_log(log, " ; ");
+ for (r = 0; r < REGS_NUM; r++) {
+ old_id = old->reg2expr[r];
+ new_id = new->reg2expr[r];
+ if (at == LOG_AT_TRANSFER && !reg_alive_at_succ(env, r, insn_idx))
+ continue;
+ if (at == LOG_AT_JOIN && !reg_alive_at(env, r, insn_idx))
+ continue;
+ if (same_exprs(scev, old_id, new_id))
+ continue;
+ is_self_scev = is_reg(scev, new_id, &regno) && r == regno;
+ if (old->empty && is_self_scev)
+ continue;
+ if (any_changes)
+ bpf_log(log, ", ");
+ log_reg(env, r);
+ bpf_log(log, " ");
+ log_expr(env, old_id);
+ bpf_log(log, " -> ");
+ log_expr(env, new_id);
+ any_changes = true;
+ }
+ if (old->empty && !any_changes)
+ bpf_log(log, "<empty join>");
+ bpf_log(log, "\n");
+ if (!old->empty && !any_changes)
+ bpf_vlog_reset(log, null_pos);
+}
+
+static bool is_probe_read_helper(u32 func_id)
+{
+ return func_id == BPF_FUNC_probe_read ||
+ func_id == BPF_FUNC_probe_read_kernel ||
+ func_id == BPF_FUNC_probe_read_user ||
+ func_id == BPF_FUNC_probe_read_str ||
+ func_id == BPF_FUNC_probe_read_kernel_str ||
+ func_id == BPF_FUNC_probe_read_user_str;
+}
+
+/*
+ * If instruction is an indirect write to stack, invalidate SCEVs for spi's
+ * that this instruction can touch.
+ */
+static void reset_scevs_at_indirect_writes(struct bpf_verifier_env *env, struct env *cur_env, int idx)
+{
+ struct bpf_insn *insn = &env->prog->insnsi[idx];
+ struct scev *scev = env->scev;
+ u8 class = BPF_CLASS(insn->code);
+ const unsigned long *mask;
+ u32 spi, reg;
+ bool opaque;
+
+ /* Direct fp stores are fine. */
+ if ((class == BPF_ST || class == BPF_STX) && insn->dst_reg == BPF_REG_FP)
+ return;
+
+ mask = bpf_may_write_mask(env, idx);
+ opaque = bpf_helper_call(insn) && is_probe_read_helper(insn->imm);
+ for_each_set_bit(spi, mask, MAX_BPF_STACK_SLOTS) {
+ reg = MAX_BPF_REG + spi;
+ if (cur_env->reg2expr[reg] == UNKNOWN_EXPR_ID)
+ continue;
+ replace_reg(scev, cur_env, reg, opaque ? OPAQUE_EXPR_ID : UNKNOWN_EXPR_ID);
+ }
+}
+
+/*
+ * Collect loop-entry registers referenced by expr 'id', best effort.
+ * DEPTH_LIMIT may leave dependencies unrecorded. This mask only avoids
+ * widening that would lose stack-access precision; missing dependencies
+ * may cause false rejections, but this is not a soundness issue.
+ */
+static void or_expr_regs(struct bpf_verifier_env *env, u32 id, unsigned long *mask)
+{
+ struct scev *scev = env->scev;
+ u32 order;
+
+ scev->stack_sz = 0;
+ expr_stack_push(scev, id);
+ while (expr_next(scev, &id, &order)) {
+ if ((order & PRE) && scev->exprs[id].op == REG)
+ __set_bit(scev->exprs[id].params[0], mask);
+ }
+}
+
+/* Mask of argument registers (R1..R5) a call at 'idx' passes by register. */
+static u16 call_params_mask(struct bpf_verifier_env *env, int idx)
+{
+ struct bpf_insn *insn = &env->prog->insnsi[idx];
+ struct bpf_call_summary cs;
+ int n = bpf_get_call_summary(env, insn, &cs) ? cs.arg_slot_cnt : MAX_BPF_FUNC_REG_ARGS;
+
+ return n ? GENMASK(BPF_REG_1 + n - 1, BPF_REG_1) : 0;
+}
+
+/*
+ * For instructions like:
+ * - *(u64 *)(rBase + off) = rX
+ * - rX = *(u64 *)(rBase + off)
+ * - calls that construct objects on stack (e.g. dynptr_from_mem(rBase, ...))
+ * When 'rBase' can be a stack pointer and is derived from some registers Rs
+ * defined at loop entry, record Rs into 'mask'.
+ */
+static void collect_store_base_regs(struct bpf_verifier_env *env,
+ struct env *cur_env, int idx, unsigned long *mask)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_insn *insn = &env->prog->insnsi[idx];
+ u8 class = BPF_CLASS(insn->code);
+ u8 size = BPF_SIZE(insn->code);
+ u16 base_regs = 0;
+ u32 r;
+
+ if (size == BPF_W || size == BPF_DW) {
+ if ((class == BPF_STX || class == BPF_ST) && insn->dst_reg != BPF_REG_FP)
+ base_regs |= BIT(insn->dst_reg);
+ else if (class == BPF_LDX && insn->src_reg != BPF_REG_FP)
+ base_regs |= BIT(insn->src_reg);
+ }
+
+ if (class == BPF_JMP && BPF_OP(insn->code) == BPF_CALL &&
+ bpf_needs_fixed_stack_off(env, idx))
+ base_regs |= call_params_mask(env, idx);
+
+ base_regs &= aux[idx].stack_ptrs;
+ for (r = 0; r < MAX_BPF_REG; r++)
+ if (base_regs & BIT(r))
+ or_expr_regs(env, cur_env->reg2expr[r], mask);
+}
+
+/* Find the topmost loop header containing idx inside cur_header, or -1 if none. */
+static int topmost_nested_loop(struct bpf_verifier_env *env, int idx, int cur_header)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ int header;
+
+ for (header = bpf_loop_at_index(env, idx); header >= 0;
+ header = aux[header].loop_header)
+ if (aux[header].loop_header == cur_header)
+ return header;
+
+ return -1;
+}
+
+/*
+ * Join cur_env into the successor's environment and schedule their traversal.
+ * Map successors in nested loops to their topmost nested header.
+ * Ignore successors outside the current loop and its nested loops.
+ */
+static int join_successor(struct bpf_verifier_env *env, int cur_header, int succ_idx,
+ struct env *old_env, struct env *cur_env)
+{
+ bool log_level2 = env->log.level & BPF_LOG_LEVEL2;
+ struct scev *scev = env->scev;
+ int err, nested_header;
+ struct env *succ_env;
+
+ /*
+ * There are several possibilities for a successor:
+ * - succ_idx can be a part of a loop outside of the cur_header's loop,
+ * such edges are ignored.
+ * - succ_idx can be a part of the same loop as cur_header,
+ * for such edges succ_idx environment is updated:
+ * e[succ_idx] = join(e[succ_idx], cur_env)
+ * - succ_idx can be a part of some loop inner to cur_header,
+ * in such a case there exists some loop header H,
+ * such that H.loop_header == cur_header
+ * and H is the same as succ_idx's loop or contains it.
+ */
+ if (bpf_loop_at_index(env, succ_idx) != cur_header) {
+ /*
+ * topmost_nested_loop() either finds H or returns -1,
+ * in case if succ_idx is a part of a loop outer to cur_header.
+ */
+ nested_header = topmost_nested_loop(env, succ_idx, cur_header);
+ if (nested_header < 0)
+ return 0;
+ succ_idx = nested_header;
+ }
+ succ_env = get_loop_env(scev, cur_header, succ_idx);
+ if (!succ_env)
+ return -ENOMEM;
+ if (log_level2)
+ memcpy(old_env, succ_env, sizeof(*old_env));
+ err = join(scev, succ_env, cur_env);
+ if (err)
+ return err;
+ err = worklist_push(env, cur_header, succ_idx);
+ if (err)
+ return err;
+ if (log_level2)
+ log_env_changes(env, LOG_AT_JOIN, old_env, succ_env, succ_idx);
+ return 0;
+}
+
+/* Unsupported loops get only an unknown header environment. */
+static bool can_compute_loop_scev(const struct bpf_loop *loop)
+{
+ return !loop->irreducible && !loop->exits_overflow;
+}
+
+static int compute_scev_for_loop(struct bpf_verifier_env *env, int cur_header)
+{
+ struct scev_worklist *worklist = &env->scev->worklist;
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_loop *cur_loop = aux[cur_header].loop;
+ struct env *header_env;
+ struct scev *scev = env->scev;
+ struct bpf_loop *nested_loop;
+ struct env *cur_env = NULL;
+ struct env *old_env = NULL;
+ struct bpf_iarray *succ;
+ bool log_level2 = env->log.level & BPF_LOG_LEVEL2;
+ int s, i, err, idx, succ_idx;
+ u32 r;
+
+ if (log_level2)
+ bpf_log(&env->log, "Computing SCEV for loop at %d:\n", cur_header);
+
+ /*
+ * For irreducible loops, and for loops nesting a loop with a truncated
+ * exit list, just assume that everything is clobbered for now.
+ */
+ if (!can_compute_loop_scev(cur_loop)) {
+ header_env = get_loop_env(scev, cur_header, cur_header);
+ if (!header_env)
+ return -ENOMEM;
+ /* The freshly allocated environment has all expressions unknown. */
+ bitmap_fill(cur_loop->store_base_regs, REGS_NUM);
+ header_env->empty = false;
+ return 0;
+ }
+
+ cur_env = kzalloc(sizeof(*cur_env), GFP_KERNEL_ACCOUNT);
+ if (!cur_env)
+ goto nomem;
+ if (log_level2) {
+ old_env = kzalloc(sizeof(*old_env), GFP_KERNEL_ACCOUNT);
+ if (!old_env)
+ goto nomem;
+ }
+ header_env = get_loop_env(scev, cur_header, cur_header);
+ if (!header_env)
+ goto nomem;
+ err = setup_initial_loop_env(env, header_env, cur_header);
+ if (err)
+ goto out;
+ err = worklist_push(env, cur_header, cur_header);
+ if (err)
+ goto out;
+
+ while (worklist->nr) {
+ idx = *min_heap_peek_inline(worklist);
+ min_heap_pop_inline(worklist, &worklist_cb, env->cfg.postorder_nums);
+
+ /* join_successor() maps nested-loop entries to their representative header. */
+ nested_loop = idx != cur_header ? aux[idx].loop : NULL;
+ memcpy(cur_env, find_loop_env(scev, cur_header, idx), sizeof(*cur_env));
+ if (nested_loop) {
+ /*
+ * Process nested loop as a single instruction by
+ * forgetting anything non-invariant in the nested loop
+ */
+ if (log_level2)
+ memcpy(old_env, cur_env, sizeof(*old_env));
+ /* Pull the nested loop's stack-store base dependencies up. */
+ for_each_set_bit(r, nested_loop->store_base_regs, BPF_SCEV_REGS_NUM)
+ or_expr_regs(env, cur_env->reg2expr[r], cur_loop->store_base_regs);
+ forget_non_invariants(env, cur_env, idx);
+ if (log_level2)
+ log_env_changes(env, LOG_AT_TRANSFER, old_env, cur_env, idx);
+ /*
+ * Treat nested loop exits as successors,
+ * join cur_env into successor's envs.
+ */
+ for (i = 0; i < nested_loop->exits_cnt; i++) {
+ s = nested_loop->exits[i].to;
+ err = join_successor(env, cur_header, s, old_env, cur_env);
+ if (err)
+ goto out;
+ }
+ } else {
+ /*
+ * Iterate instructions within a single basic block
+ * starting at 'idx' mutating 'cur_env'.
+ */
+ for (;;) {
+ if (log_level2)
+ memcpy(old_env, cur_env, sizeof(*old_env));
+ collect_store_base_regs(env, cur_env, idx, cur_loop->store_base_regs);
+ err = transfer(env, cur_env, idx);
+ if (err)
+ goto out;
+ reset_scevs_at_indirect_writes(env, cur_env, idx);
+ if (log_level2)
+ log_env_changes(env, LOG_AT_TRANSFER, old_env, cur_env, idx);
+ succ = bpf_insn_successors(env, idx);
+ if (succ->cnt != 1)
+ break;
+ succ_idx = succ->items[0];
+ if (aux[idx].bb_end || aux[succ_idx].need_scev ||
+ bpf_loop_at_index(env, succ_idx) != cur_header)
+ break;
+ idx = succ_idx;
+ }
+ /* Join cur_env into basic block successor's envs. */
+ iarray_for_each(s, succ) {
+ err = join_successor(env, cur_header, s, old_env, cur_env);
+ if (err)
+ goto out;
+ }
+ }
+ }
+
+ err = 0;
+out:
+ kfree(cur_env);
+ kfree(old_env);
+ return err;
+nomem:
+ err = -ENOMEM;
+ goto out;
+}
+
+static void mark_latches(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_loop *loop;
+ int len = env->prog->len;
+ int i, j, k, from;
+
+ for (i = 0; i < len; i++) {
+ loop = aux[i].loop;
+ if (!loop)
+ continue;
+ aux[i].need_scev = true;
+ if (!can_compute_loop_scev(loop))
+ continue;
+ /*
+ * Make sure to only set .need_scev for exits and latches
+ * belonging to the loop at `i`. If can_compute_loop_scev()
+ * fails for a loop, it should hide it's instructions from
+ * compute_insn_scevs().
+ */
+ for (j = 0; j < loop->backedges_cnt; j++)
+ for (k = 0; k < loop->backedges[j].latches_cnt; k++)
+ aux[loop->backedges[j].latches[k]].need_scev = true;
+ for (j = 0; j < loop->exits_cnt; j++) {
+ from = loop->exits[j].from;
+ if (bpf_loop_at_index(env, from) == i)
+ aux[from].need_scev = true;
+ }
+ }
+}
+
+static bool is_any_imm_reg_opaque(struct scev *scev, u32 id)
+{
+ u32 l, r, reg, order;
+ s64 imm;
+
+ scev->stack_sz = 0;
+ expr_stack_push(scev, id);
+ while (expr_next(scev, &id, &order)) {
+ if (order & DEPTH_LIMIT)
+ return false;
+ if (!(order & PRE) ||
+ is_imm(scev, id, &imm) ||
+ is_reg(scev, id, &reg) ||
+ is_opaque(scev, id) ||
+ is_any(scev, id, &l, &r))
+ continue;
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Can implement explicit stack version, but it is harder to read.
+ * Stick with recursive version for now.
+ */
+static int transform_expr_once(struct scev *scev, u32 lvl, u32 root, void *priv,
+ int (*fn)(struct scev *scev, u32 id, void *priv))
+{
+ struct expr expr;
+ int p0, p1, id;
+
+ if (lvl >= EXPR_STACK_DEPTH)
+ return UNKNOWN_EXPR_ID;
+
+ expr = scev->exprs[root]; /* snapshot the expr before potential realloc */
+ switch (op_params_num(expr.op)) {
+ case 0:
+ id = root;
+ break;
+ case 1:
+ p0 = transform_expr_once(scev, lvl + 1, expr.params[0], priv, fn);
+ id = expr1(scev, expr.op, p0);
+ break;
+ case 2:
+ p0 = transform_expr_once(scev, lvl + 1, expr.params[0], priv, fn);
+ p1 = transform_expr_once(scev, lvl + 1, expr.params[1], priv, fn);
+ id = expr2(scev, expr.op, p0, p1);
+ break;
+ }
+ return id < 0 ? id : fn(scev, id, priv);
+}
+
+static int transform_expr(struct scev *scev, u32 root, void *priv,
+ int (*fn)(struct scev *scev, u32 id, void *priv))
+{
+ int i = 0, id_old, id_new = root;
+
+ do {
+ /*
+ * The depth limit is somewhat arbitrary and is essentially a failsafe.
+ * oscillation should not really happen. Still, it seems harsh to error out.
+ */
+ if (i++ == 4 * EXPR_STACK_DEPTH)
+ return UNKNOWN_EXPR_ID;
+ id_old = id_new;
+ id_new = transform_expr_once(scev, 0, id_old, priv, fn);
+ if (id_new < 0)
+ return id_new;
+ } while (id_old != id_new);
+ return id_new;
+}
+
+static int simplify(struct scev *scev, u32 id, void *priv)
+{
+ int a, b, l, r, t, base, slope;
+ s64 imm1, imm2;
+
+ /* (+ (linear base slope) imm) -> (linear (+ base imm) slope) */
+ if (is_add(scev, id, &l, &r) &&
+ is_linear(scev, l, &base, &slope) &&
+ is_imm(scev, r, &imm1))
+ return expr2(scev, LINEAR_SCEV, expr2(scev, BPF_ADD, base, r), slope);
+
+ /* (+ imm imm) -> imm */
+ if (is_add(scev, id, &l, &r) &&
+ is_imm(scev, l, &imm1) &&
+ is_imm(scev, r, &imm2))
+ return imm_expr(scev, imm1 + imm2);
+
+ /* (zext32 (imm X)) -> (imm (u32)X) */
+ if (is_zext32(scev, id, &l) && is_imm(scev, l, &imm1))
+ return imm_expr(scev, (u64)(u32)imm1);
+
+ /*
+ * (zext32 (+ (zext32 $a) $b)) -> (zext32 (+ $a $b))
+ * (zext32 (+ $a (zext32 $b))) -> (zext32 (+ $a $b))
+ */
+ if ((is_zext32(scev, id, &t) && is_add(scev, t, &l, &b) && is_zext32(scev, l, &a)) ||
+ (is_zext32(scev, id, &t) && is_add(scev, t, &a, &r) && is_zext32(scev, r, &b))) {
+ t = expr2(scev, BPF_ADD, a, b);
+ t = expr1(scev, ZEXT32, t);
+ return t;
+ }
+
+ return id;
+}
+
+static int compute_header_scevs(struct bpf_verifier_env *env, struct env *header_env)
+{
+ struct scev *scev = env->scev;
+ int l, r, t, id, ra, rb, ra_expr;
+ s64 imm;
+
+ /*
+ * Note:
+ * reg2expr[ra] describes a change in rA's value over a single loop iteration.
+ * It does not account for rA's entry value. However, the SCEV constructed for rA
+ * must include an additional rA entry edge. For example:
+ *
+ * r0 = ..., r1 = 7
+ * l: if r0 == 10 goto e // reg2expr[r1] == (imm 42) => reg2scev[r1] == (any r1 42)
+ * r0 += 1
+ * r1 = 42
+ * goto l
+ * e:
+ * ... at exit r1 is in {7,42} ...
+ */
+ for (ra = 0; ra < REGS_NUM; ra++) {
+ ra_expr = transform_expr(scev, header_env->reg2expr[ra], NULL, simplify);
+ if (ra_expr < 0)
+ return ra_expr;
+ header_env->reg2expr[ra] = ra_expr;
+ /* rA = (+ rA IMM) -> (linear rA IMM) */
+ if (is_add(scev, ra_expr, &l, &r) &&
+ is_reg(scev, l, &rb) &&
+ is_imm(scev, r, &imm) &&
+ ra == rb) {
+ id = expr2(scev, LINEAR_SCEV, l, r);
+ if (id < 0)
+ return id;
+ header_env->reg2scev[ra] = id;
+ continue;
+ }
+ /* rA = (is_zext32 (+ rA IMM)) -> (is_zext32 (linear rA IMM)) */
+ if (is_zext32(scev, ra_expr, &t) &&
+ is_add(scev, t, &l, &r) &&
+ is_reg(scev, l, &rb) &&
+ is_imm(scev, r, &imm) &&
+ ra == rb) {
+ id = expr2(scev, LINEAR_SCEV, l, r);
+ id = expr1(scev, ZEXT32, id);
+ if (id < 0)
+ return id;
+ header_env->reg2scev[ra] = id;
+ continue;
+ }
+ /* rA = rA -> rA*/
+ if (is_reg(scev, ra_expr, &rb) && ra == rb) {
+ header_env->reg2scev[ra] = ra_expr;
+ continue;
+ }
+ /* rA = (any 1 2 3 4 ...) -> (any rA 1 2 3 4 ...) */
+ if (is_any_imm_reg_opaque(scev, ra_expr)) {
+ id = mk_any(scev, expr1(scev, REG, ra), ra_expr);
+ if (id < 0)
+ return id;
+ header_env->reg2scev[ra] = id;
+ continue;
+ }
+
+ }
+ return 0;
+}
+static int instantiate_header_scevs(struct scev *scev, u32 id, void *priv)
+{
+ u32 reg, *reg2scev = priv;
+
+ /* (reg r) -> ... equation at header ... */
+ if (is_reg(scev, id, &reg))
+ return reg2scev[reg];
+ /* keep expression shapes */
+ return id;
+}
+
+static int compute_insn_scevs(struct bpf_verifier_env *env, struct env *eheader, struct env *einsn)
+{
+ struct scev *scev = env->scev;
+ int id, reg;
+
+ for (reg = 0; reg < REGS_NUM; reg++) {
+ id = einsn->reg2expr[reg];
+ id = transform_expr_once(scev, 0, id, eheader->reg2scev, instantiate_header_scevs);
+ if (id < 0)
+ return id;
+ id = transform_expr(scev, id, NULL, simplify);
+ if (id < 0)
+ return id;
+ einsn->reg2scev[reg] = id;
+ }
+
+ return 0;
+}
+
+static int log_scevs(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_verifier_log *log = &env->log;
+ struct env *header_env, *latch_env;
+ struct scev *scev = env->scev;
+ struct bpf_loop *loop;
+ int i, j, k, len, latch;
+
+ len = env->prog->len;
+ for (i = 0; i < len; i++) {
+ loop = aux[i].loop;
+ if (!loop)
+ continue;
+ header_env = find_header_env(scev, i);
+ bpf_log(log, "scev at header %d:\n", i);
+ print_env(env, header_env, i);
+ if (!can_compute_loop_scev(loop))
+ continue;
+ for (j = 0; j < loop->backedges_cnt; j++) {
+ for (k = 0; k < loop->backedges[j].latches_cnt; k++) {
+ latch = loop->backedges[j].latches[k];
+ bpf_log(log, " scev at %s %d:\n", k ? "exit" : "latch", latch);
+ latch_env = find_loop_env(scev, i, latch);
+ if (verifier_bug_if(!latch_env, env, "i=%d, latch=%d", i, latch))
+ return -EFAULT;
+ print_env(env, latch_env, latch);
+ }
+ }
+ }
+ return 0;
+}
+
+int bpf_compute_scev(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct scev *scev = env->scev;
+ int *postorder = env->cfg.insn_postorder;
+ int cnt = env->cfg.cur_postorder;
+ int i, idx, err, header;
+
+ mark_latches(env);
+ /*
+ * Visit loop headers in postorder, to guarantee that scevs
+ * for innermost loops are computed first.
+ */
+ for (i = 0; i < cnt; i++) {
+ idx = postorder[i];
+ if (!aux[idx].loop)
+ continue;
+ err = compute_scev_for_loop(env, idx);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Compute scevs from exprs collected on a previous step. Iterate instructions in
+ * reverse post-order so that each loop header is processed before instructions
+ * reachable from it.
+ */
+ for (i = cnt - 1; i >= 0; i--) {
+ idx = postorder[i];
+ if (!aux[idx].need_scev)
+ continue;
+ header = bpf_loop_at_index(env, idx);
+ err = aux[idx].loop
+ ? compute_header_scevs(env, find_header_env(scev, header))
+ : compute_insn_scevs(env,
+ find_header_env(scev, header),
+ find_loop_env(scev, header, idx));
+ if (err)
+ return err;
+ }
+
+ for (i = 0; i < env->prog->len; i++)
+ if (aux[i].loop)
+ aux[i].prune_point = true;
+
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ err = log_scevs(env);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+void bpf_free_scev(struct bpf_verifier_env *env)
+{
+ struct scev *scev = env->scev;
+ struct insn_envs *envs;
+ int i;
+ u32 j;
+
+ if (!scev)
+ return;
+ for (i = 0; i < scev->envs_cnt; i++) {
+ envs = scev->envs[i];
+ if (!envs)
+ continue;
+ for (j = 0; j < envs->cnt; j++)
+ kfree(envs->entries[j].env);
+ kfree(envs);
+ }
+ if (scev->exprs_ht)
+ for (i = 0; i < scev->exprs_ht_cnt; i++)
+ kvfree(scev->exprs_ht[i]);
+ kvfree(scev->exprs_ht);
+ kvfree(scev->worklist.data);
+ kvfree(scev->envs);
+ kvfree(scev->exprs);
+ kvfree(scev->discovered);
+ kfree(scev);
+ env->scev = NULL;
+}
+
+int bpf_init_scev(struct bpf_verifier_env *env)
+{
+ struct scev *scev;
+
+ scev = kzalloc(sizeof(struct scev), GFP_KERNEL_ACCOUNT);
+ if (!scev)
+ return -ENOMEM;
+ env->scev = scev;
+ bpf_mark_reg_known_scalar(&scev->zero_reg, 0);
+ bpf_mark_reg_unknown_imprecise(&scev->unknown_reg);
+ /* A power of two for hash & (exprs_ht_cnt - 1)` indexing. */
+ scev->exprs_ht_cnt = roundup_pow_of_two(max(256U, DIV_ROUND_UP(env->prog->len, 4)));
+ scev->exprs_ht = kvcalloc(scev->exprs_ht_cnt, sizeof(*scev->exprs_ht), GFP_KERNEL_ACCOUNT);
+ if (!scev->exprs_ht)
+ goto nomem;
+ if (expr0(scev, UNKNOWN) < 0 || expr0(scev, OPAQUE) < 0)
+ goto nomem;
+ scev->envs = kvcalloc(env->prog->len, sizeof(*scev->envs), GFP_KERNEL_ACCOUNT);
+ scev->discovered = kvcalloc(env->prog->len, sizeof(*scev->discovered), GFP_KERNEL_ACCOUNT);
+ if (!scev->envs || !scev->discovered)
+ goto nomem;
+ /*
+ * Remember original program length, in case bpf_free_scev()
+ * is called after bpf program rewrites that increase program
+ * length.
+ */
+ scev->envs_cnt = env->prog->len;
+ return 0;
+nomem:
+ bpf_free_scev(env);
+ return -ENOMEM;
+}
+
+static struct bpf_stack_state *scev_regno_to_stack(const struct bpf_func_state *st, u32 r)
+{
+ int spi, slots_available;
+
+ if (r < MAX_BPF_REG)
+ return NULL;
+
+ slots_available = st->allocated_stack / BPF_REG_SIZE;
+ spi = r - MAX_BPF_REG;
+ if (spi < slots_available)
+ return &st->stack[spi];
+
+ return NULL;
+}
+
+static struct bpf_reg_state *scev_regno_to_reg(struct bpf_func_state *st, u32 r)
+{
+ return r < MAX_BPF_REG ? &st->regs[r] : NULL;
+}
+
+/* Linear widening requires a physical register or a full spill. */
+static struct bpf_reg_state *scev_reg_for_linear(struct bpf_func_state *st, u32 r)
+{
+ struct bpf_stack_state *stack;
+
+ if (r < MAX_BPF_REG)
+ return scev_regno_to_reg(st, r);
+ stack = scev_regno_to_stack(st, r);
+ if (!stack || bpf_spill_size(stack) != BPF_REG_SIZE)
+ return NULL;
+ return &stack->spilled_ptr;
+}
+
+/*
+ * Return a borrowed value; never use it as a widening destination.
+ * NULL denotes no readable value.
+ */
+static const struct bpf_reg_state *
+scev_reg_for_read(struct bpf_verifier_env *env, const struct bpf_func_state *st, u32 r)
+{
+ const struct bpf_stack_state *stack;
+ bool zero = true;
+ int i;
+
+ if (r < MAX_BPF_REG)
+ return st->regs[r].type == NOT_INIT ? NULL : &st->regs[r];
+ if (r >= REGS_NUM || r - MAX_BPF_REG >= env->stack_limit / BPF_REG_SIZE)
+ return NULL;
+
+ stack = scev_regno_to_stack(st, r);
+ if (!stack)
+ return &env->scev->unknown_reg;
+ if (bpf_spill_size(stack) == BPF_REG_SIZE)
+ return &stack->spilled_ptr;
+
+ /* Partial spills do not describe the value of the whole slot. */
+ for (i = 0; i < BPF_REG_SIZE; i++) {
+ switch (stack->slot_type[i]) {
+ case STACK_ZERO:
+ break;
+ case STACK_INVALID:
+ case STACK_POISON:
+ case STACK_MISC:
+ zero = false;
+ break;
+ case STACK_SPILL:
+ if (!bpf_is_spilled_scalar_reg(stack))
+ return NULL;
+ zero = false;
+ break;
+ default:
+ return NULL;
+ }
+ }
+ return zero ? &env->scev->zero_reg : &env->scev->unknown_reg;
+}
+
+/* Materialize a writable value in the current state, growing its stack if needed. */
+static int scev_reg_for_write(struct bpf_verifier_env *env, struct bpf_func_state *st,
+ u32 r, struct bpf_reg_state **out)
+{
+ const struct bpf_reg_state *value;
+ struct bpf_stack_state *stack;
+ u32 spi;
+ int err;
+
+ *out = scev_regno_to_reg(st, r);
+ if (*out)
+ return 0;
+ if (r >= REGS_NUM || r - MAX_BPF_REG >= env->stack_limit / BPF_REG_SIZE)
+ return -EINVAL;
+
+ spi = r - MAX_BPF_REG;
+ err = bpf_grow_stack_state(env, st, (spi + 1) * BPF_REG_SIZE);
+ if (err)
+ return err;
+
+ /* Resolve pointers after growth, which may relocate the stack. */
+ value = scev_reg_for_read(env, st, r);
+ if (!value)
+ return -EINVAL;
+ stack = scev_regno_to_stack(st, r);
+ stack->spilled_ptr = *value;
+ memset(stack->slot_type, STACK_SPILL, sizeof(stack->slot_type));
+ *out = &stack->spilled_ptr;
+ return 0;
+}
+
+static bool scev_reg_alive(struct bpf_verifier_env *env, struct bpf_verifier_state *st, u32 r)
+{
+ int insn_idx = bpf_frame_insn_idx(st, st->curframe);
+ u16 live_regs = env->insn_aux_data[insn_idx].live_regs_before;
+ int spi;
+
+ if (r < MAX_BPF_REG) {
+ return BIT(r) & live_regs;
+ } else {
+ spi = r - MAX_BPF_REG;
+ return bpf_stack_slot_alive(env, st->curframe, spi * 2) ||
+ bpf_stack_slot_alive(env, st->curframe, spi * 2 + 1);
+ }
+}
+
+/*
+ * Latch is a condition deciding if execution remains inside a loop.
+ * Linear latch represents a condition 'if <reg> <op> <loop invariant> goto <loop-header>',
+ * where equation '<base> + i * <step> <op> <bound>' describes values taken by register <reg>,
+ * 'i' is the loop iteration number, starting from 0.
+ * 'zext32' indicates the latch is for (zext32 (linear ...)) SCEV.
+ * 'jmp32' indicates that the latch is a JMP32 instruction.
+ */
+struct linear_latch {
+ u32 insn_idx;
+ u32 base_expr;
+ u32 step_expr;
+ u32 bound_expr;
+ u8 op;
+ bool zext32;
+ bool jmp32;
+};
+
+static bool loop_invariant(struct scev *scev, u32 id)
+{
+ s64 imm;
+ u32 reg;
+
+ return is_reg(scev, id, &reg) || is_imm(scev, id, &imm);
+}
+
+static int match_linear_latch(struct bpf_verifier_env *env,
+ u32 header,
+ u32 latch_idx,
+ struct linear_latch *latch)
+{
+ struct bpf_insn *insn = &env->prog->insnsi[latch_idx];
+ struct scev *scev = env->scev;
+ struct env *latch_env;
+ u32 true_branch_loop;
+ u32 true_branch_tgt;
+ u32 src_reg_scev;
+ u32 dst_reg_scev;
+ u32 t, l, r, op;
+ int id;
+
+ op = BPF_OP(insn->code);
+ latch->jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
+ /* Flip the condition if true branch jumps out of the loop. */
+ true_branch_tgt = latch_idx + bpf_jmp_offset(insn) + 1;
+ true_branch_loop = bpf_loop_at_index(env, true_branch_tgt);
+ if (true_branch_loop != header &&
+ !bpf_is_nested_loop(env, true_branch_loop, header))
+ op = bpf_rev_opcode(op);
+ switch (op) {
+ case BPF_JSLT:
+ case BPF_JSLE:
+ case BPF_JSGT:
+ case BPF_JSGE:
+ case BPF_JLT:
+ case BPF_JLE:
+ case BPF_JGT:
+ case BPF_JGE:
+ case BPF_JNE:
+ break;
+ default:
+ return false;
+ }
+
+ latch->insn_idx = latch_idx;
+
+ latch_env = find_loop_env(scev, header, latch_idx);
+ if (verifier_bug_if(!latch_env, env, "header_idx=%d, latch_idx=%d\n", header, latch_idx))
+ return -EFAULT;
+ dst_reg_scev = latch_env->reg2scev[insn->dst_reg];
+ src_reg_scev = latch_env->reg2scev[insn->src_reg];
+ /*
+ * Make sure (linear ...) is in dst_reg_scev:
+ * 'if <invariant> <op> <linear>' ⇔
+ * 'if <linear> <flip(op)> <invariant>'.
+ */
+ if (BPF_SRC(insn->code) == BPF_X &&
+ is_linear_or_linear32(scev, src_reg_scev, &l, &r, &latch->zext32)) {
+ swap(dst_reg_scev, src_reg_scev);
+ op = bpf_flip_opcode(op);
+ }
+ latch->op = op;
+ /*
+ * simplify() produces two shapes for linear latches:
+ * - (linear register step)
+ * ^----------------------------+-- base_expr
+ * base_expr |
+ * - (linear (+ register imm) step) |
+ * ^----------------------------'
+ */
+ if (!is_linear_or_linear32(scev, dst_reg_scev,
+ &latch->base_expr, &latch->step_expr, &latch->zext32))
+ return false;
+
+ if (BPF_SRC(insn->code) == BPF_K) {
+ id = imm_expr(scev, insn->imm);
+ if (id < 0)
+ return id;
+ latch->bound_expr = id;
+ } else {
+ latch->bound_expr = src_reg_scev;
+ }
+
+ if (!loop_invariant(scev, latch->step_expr) ||
+ !loop_invariant(scev, latch->bound_expr))
+ return false;
+
+ /* (linear register step) */
+ if (is_reg(scev, latch->base_expr, &t))
+ return true;
+
+ /* (linear (+ register imm) step) */
+ if (is_add(scev, latch->base_expr, &l, &r) &&
+ is_reg(scev, l, &t) &&
+ loop_invariant(scev, r))
+ return true;
+
+ return false;
+}
+
+struct scev_value {
+ u64 value; /* scalar value or a pointer offset */
+ int ptr_reg; /* SCEV register identifying the pointer origin, or -1. */
+};
+
+static void log_scev_value(struct bpf_verifier_env *env, struct bpf_func_state *st,
+ const struct scev_value *v)
+{
+ const struct bpf_reg_state *reg;
+
+ if (v->ptr_reg >= 0) {
+ reg = scev_reg_for_read(env, st, v->ptr_reg);
+ log_reg(env, v->ptr_reg);
+ bpf_log(&env->log, " (%s)", reg ? reg_type_str(env, reg->type) : "unavailable");
+ } else {
+ bpf_log(&env->log, "scalar %llu", v->value);
+ }
+}
+
+static void log_incompatible_scev_values(struct bpf_verifier_env *env, const char *reason,
+ struct bpf_func_state *st,
+ const struct scev_value *a, const struct scev_value *b)
+{
+ bpf_log(&env->log, "scev: %s ", reason);
+ log_scev_value(env, st, a);
+ bpf_log(&env->log, ", ");
+ log_scev_value(env, st, b);
+ bpf_log(&env->log, "\n");
+}
+
+static bool scev_value_compatible(struct bpf_verifier_env *env, struct bpf_func_state *st,
+ const struct scev_value *a, const struct scev_value *b)
+{
+ const struct bpf_reg_state *ra, *rb;
+
+ if (a->ptr_reg < 0 || b->ptr_reg < 0) {
+ if (a->ptr_reg < 0 && b->ptr_reg < 0)
+ return true;
+ goto incompatible;
+ }
+ ra = scev_reg_for_read(env, st, a->ptr_reg);
+ rb = scev_reg_for_read(env, st, b->ptr_reg);
+ if (ra && rb && bpf_same_memory_origin(ra, rb))
+ return true;
+
+incompatible:
+ if (env->log.level & BPF_LOG_LEVEL2)
+ log_incompatible_scev_values(env, "incompatible latch operands", st, a, b);
+ return false;
+}
+
+static bool scev_value_add(struct bpf_verifier_env *env, struct bpf_func_state *st,
+ const struct scev_value *a, const struct scev_value *b,
+ struct scev_value *result)
+{
+ if (a->ptr_reg >= 0 && b->ptr_reg >= 0) {
+ if (env->log.level & BPF_LOG_LEVEL2)
+ log_incompatible_scev_values(env, "can't add two pointers", st, a, b);
+ return false;
+ }
+ result->value = a->value + b->value;
+ result->ptr_reg = a->ptr_reg >= 0 ? a->ptr_reg : b->ptr_reg;
+ return true;
+}
+
+static bool eval_expr(struct bpf_verifier_env *env, struct scev *scev,
+ struct bpf_func_state *st, u32 id, struct scev_value *result)
+{
+ struct scev_value lval, rval;
+ const struct bpf_reg_state *reg;
+ u32 l, r, regno;
+ s64 imm;
+
+ if (is_reg(scev, id, &regno)) {
+ reg = scev_reg_for_read(env, st, regno);
+ if (!reg || reg->type == NOT_INIT || !tnum_is_const(reg->var_off))
+ return false;
+ result->value = reg->var_off.value;
+ result->ptr_reg = reg->type == SCALAR_VALUE ? -1 : (int)regno;
+ return true;
+ } else if (is_imm(scev, id, &imm)) {
+ result->value = imm;
+ result->ptr_reg = -1;
+ return true;
+ } else if (is_add(scev, id, &l, &r) &&
+ eval_expr(env, scev, st, l, &lval) &&
+ eval_expr(env, scev, st, r, &rval)) {
+ return scev_value_add(env, st, &lval, &rval, result);
+ }
+ return false;
+}
+
+static bool is_signed_op(u8 op)
+{
+ switch (op) {
+ case BPF_JSLT:
+ case BPF_JSLE:
+ case BPF_JSGT:
+ case BPF_JSGE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool compute_backedge_taken_count(u8 op, u64 initial, u64 step, u64 bound, u64 *result)
+{
+ u64 rem, diff, backedge_taken_count;
+ bool up, down;
+
+ if (step == 0)
+ return false;
+ down = (s64)step < 0;
+ up = (s64)step > 0;
+ diff = bound - initial;
+ switch (op) {
+ case BPF_JLT:
+ /*
+ * E.g.: 3 + 2*i < 7 ⇔ 2*i < 4 ⇔ i ∈ [0,1].
+ * Here the inequality describes a set of values `i` might take
+ * while the latch condition remains true (the backedge is taken).
+ * The backedge_taken_count is the size of this set.
+ */
+ if (/*
+ * If the condition is initially true, decreasing values won't
+ * change that without underflow. If the condition is initially false,
+ * the main pass would do a single iteration anyway.
+ */
+ down ||
+ /* Is the latch condition false on the first iteration? */
+ initial >= bound ||
+ /* Count iterations satisfying the strict inequality, including i=0. */
+ check_add_overflow(div64_u64(diff - 1, step), 1, &backedge_taken_count) ||
+ /*
+ * Check if the last iteration overflows the counter:
+ * initial + backedge_taken_count * step > U64_MAX
+ */
+ backedge_taken_count > div64_u64(U64_MAX - initial, step))
+ return false;
+ break;
+ case BPF_JLE:
+ /* E.g.: 3 + 2*i ≤ 7 ⇔ 2*i ≤ 4 ⇔ i ∈ [0,1,2]. */
+ if (down || initial > bound ||
+ /* Count iterations satisfying the non-strict inequality. */
+ check_add_overflow(div64_u64(diff, step), 1, &backedge_taken_count) ||
+ backedge_taken_count > div64_u64(U64_MAX - initial, step))
+ return false;
+ break;
+ case BPF_JGT:
+ /* E.g.: 7 - 2*i > 3 ⇔ 2*i < 4 ⇔ i ∈ [0,1]. */
+ if (up || initial <= bound ||
+ check_add_overflow(div64_u64(-diff - 1, -step), 1, &backedge_taken_count) ||
+ /*
+ * The last iteration underflows the counter if:
+ * initial + backedge_taken_count * step < 0 ⇔
+ * backedge_taken_count * step < -initial ⇔
+ * backedge_taken_count > initial / -step
+ * (direction flips because step < 0)
+ */
+ backedge_taken_count > div64_u64(initial, -step))
+ return false;
+ break;
+ case BPF_JGE:
+ /* E.g.: 7 - 2*i ≥ 3 ⇔ 2*i ≤ 4 ⇔ i ∈ [0,1,2]. */
+ if (up || initial < bound ||
+ check_add_overflow(div64_u64(-diff, -step), 1, &backedge_taken_count) ||
+ backedge_taken_count > div64_u64(initial, -step))
+ return false;
+ break;
+ case BPF_JSLT:
+ if (down || (s64)initial >= (s64)bound ||
+ check_add_overflow(div64_u64(diff - 1, step), 1, &backedge_taken_count) ||
+ /*
+ * Check if the last iteration overflows the signed counter:
+ * (s64)initial + backedge_taken_count * step > S64_MAX
+ */
+ backedge_taken_count > div64_u64((u64)S64_MAX - initial, step))
+ return false;
+ break;
+ case BPF_JSLE:
+ if (down || (s64)initial > (s64)bound ||
+ check_add_overflow(div64_u64(diff, step), 1, &backedge_taken_count) ||
+ backedge_taken_count > div64_u64((u64)S64_MAX - initial, step))
+ return false;
+ break;
+ case BPF_JSGT:
+ if (up || (s64)initial <= (s64)bound ||
+ check_add_overflow(div64_u64(-diff - 1, -step), 1, &backedge_taken_count) ||
+ /*
+ * The last iteration underflows the signed counter if:
+ * (s64)initial + backedge_taken_count * step < S64_MIN ⇔
+ * backedge_taken_count * step < S64_MIN - (s64)initial ⇔
+ * backedge_taken_count > initial - (u64)S64_MIN / -step
+ * (direction flips because step < 0)
+ */
+ backedge_taken_count > div64_u64(initial - (u64)S64_MIN, -step))
+ return false;
+ break;
+ case BPF_JSGE:
+ if (up || (s64)initial < (s64)bound ||
+ check_add_overflow(div64_u64(-diff, -step), 1, &backedge_taken_count) ||
+ backedge_taken_count > div64_u64(initial - (u64)S64_MIN, -step))
+ return false;
+ break;
+ case BPF_JNE:
+ /*
+ * For a decreasing counter flip the diff and step:
+ * 7 - 2*i ≠ 3 ⇔ 2*i ≠ 7 - 3.
+ */
+ diff = down ? -diff : diff;
+ step = down ? -step : step;
+ backedge_taken_count = div64_u64_rem(diff, step, &rem);
+ if (diff == 0 || rem != 0)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ *result = backedge_taken_count;
+ return true;
+}
+
+/*
+ * Loop with post-condition:
+ *
+ * r0 = 0
+ * l: ... r0 ∈ [0,1,2] header executed 3 times
+ * r0 += 1 r0 ∈ [0,1,2]
+ * ... r0 ∈ [1,2,3]
+ * if r0 != 3 goto l r0 ∈ [1,2,3] backedge taken 2 times
+ * ... r0 ∈ [3]
+ *
+ * SCEV at header: r0 = k
+ * SCEV at latch: r0 = 1 + k
+ *
+ * Loop with pre-condition:
+ *
+ * r0 = 0
+ * l: ... r0 ∈ [0,1,2,3] header executed 4 times
+ * if r0 == 3 goto e r0 ∈ [0,1,2,3] backedge taken 3 times
+ * r0 += 1 r0 ∈ [0,1,2]
+ * ... r0 ∈ [1,2,3]
+ * goto l r0 ∈ [1,2,3]
+ * e: ... r0 ∈ [3]
+ *
+ * SCEV at header: r0 = k
+ * SCEV at latch: r0 = k
+ */
+static bool compute_max_iters(struct bpf_verifier_env *env,
+ struct bpf_func_state *st,
+ struct linear_latch *latch,
+ struct bpf_loop_iters *iters)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_loop *loop = aux[bpf_loop_at_index(env, latch->insn_idx)].loop;
+ struct scev *scev = env->scev;
+ struct scev_value initial_val, step_val, bound_val;
+ u64 step, bound, initial, total_change, backedge_taken_count;
+ u8 op = latch->op;
+
+ if (!eval_expr(env, scev, st, latch->base_expr, &initial_val) ||
+ !eval_expr(env, scev, st, latch->step_expr, &step_val) ||
+ !eval_expr(env, scev, st, latch->bound_expr, &bound_val))
+ return false;
+
+ if (step_val.ptr_reg >= 0) {
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ bpf_log(&env->log, "scev: latch step is a pointer ");
+ log_scev_value(env, st, &step_val);
+ bpf_log(&env->log, "\n");
+ }
+ return false;
+ }
+ if (!scev_value_compatible(env, st, &initial_val, &bound_val))
+ return false;
+
+ initial = initial_val.value;
+ step = step_val.value;
+ bound = bound_val.value;
+
+ /*
+ * The code below only handles mixed 64-bit/32-bit comparisons/recurrences
+ * when initial bounds and step fit in 32-bit domain.
+ */
+ if (latch->zext32 || latch->jmp32) {
+ /*
+ * With the above constraint doing `l: w0 += 1; if r0 s< 256 goto l;`
+ * is equivalent to `l: w0 += 1; if r0 < 256 goto l;`
+ */
+ if (latch->jmp32 && is_signed_op(op)) {
+ initial = (s64)(s32)(u32)initial;
+ bound = (s64)(s32)(u32)bound;
+ step = (s64)(s32)(u32)step;
+ } else {
+ initial = (u32)initial;
+ bound = (u32)bound;
+ step = (s64)(s32)(u32)step;
+ }
+ }
+ if (latch->zext32 ^ latch->jmp32 && (initial != initial_val.value ||
+ bound != bound_val.value ||
+ step != step_val.value)) {
+ if (env->log.level & BPF_LOG_LEVEL2)
+ bpf_log(&env->log,
+ "scev: mixed-width latch has operands outside 32-bit ranges\n");
+ return false;
+ }
+
+
+ if (!compute_backedge_taken_count(op, initial, step, bound, &backedge_taken_count))
+ return false;
+
+ if (latch->zext32 || latch->jmp32) {
+ /*
+ * compute_backedge_taken_count() guarantees that 'total_change'
+ * does not overflow in 64-bit domain.
+ */
+ total_change = initial + step * backedge_taken_count;
+ if (latch->jmp32 && is_signed_op(op)) {
+ if ((s64)total_change < S32_MIN || (s64)total_change > S32_MAX)
+ return false;
+ } else {
+ if (total_change > U32_MAX)
+ return false;
+ }
+ }
+
+ /*
+ * max_header_count is u32 and U32_MAX means "infinite",
+ * check to avoid overflow below.
+ */
+ if (backedge_taken_count >= U32_MAX - 1)
+ return false;
+
+ /* The header executes once on entry and once for each taken backedge, hence +1. */
+ iters->max_header_count = backedge_taken_count + 1;
+ /*
+ * The latch is a conditional jump with one jump target exiting the loop.
+ * Linear latch is matched only if the loop has a single backedge.
+ * The loop still, however can have multiple exits.
+ * In such case, conservatively assume that non-latch exit can happen
+ * at any iteration, thus setting minimal number of iterations as 0.
+ */
+ iters->min_header_count = loop->exits_cnt == 1 ? iters->max_header_count : 0;
+ return true;
+}
+
+static void mark_scev_reg_scratched(struct bpf_verifier_env *env, u32 r)
+{
+ if (r < MAX_BPF_REG)
+ mark_reg_scratched(env, r);
+ else
+ mark_stack_slot_scratched(env, r - __MAX_BPF_REG);
+}
+
+/*
+ * An unknown scalar is its own widening.
+ * Stack bytes without a value are represented by scev->unknown_reg.
+ */
+static bool is_unbound_scev_reg(struct bpf_verifier_env *env, const struct bpf_func_state *st,
+ u32 r)
+{
+ const struct bpf_reg_state *reg = scev_reg_for_read(env, st, r);
+
+ return reg && reg->type == SCALAR_VALUE && !reg->id && reg->step <= 1 &&
+ tnum_is_unknown(reg->var_off) &&
+ reg->r64.size == U64_MAX && reg->r32.size == U32_MAX;
+}
+
+/* Main logic in verifier.c forbids varying offsets for certain register types. */
+static bool is_widenable_reg_type(const struct bpf_reg_state *reg)
+{
+ if (type_may_be_null(reg->type))
+ return false;
+
+ switch (base_type(reg->type)) {
+ case SCALAR_VALUE:
+ case PTR_TO_MAP_VALUE:
+ case PTR_TO_MAP_KEY:
+ case PTR_TO_STACK:
+ case PTR_TO_PACKET:
+ case PTR_TO_PACKET_META:
+ case PTR_TO_MEM:
+ case PTR_TO_BUF:
+ case PTR_TO_BTF_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void scratch_widened_reg_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
+{
+ switch (base_type(reg->type)) {
+ case SCALAR_VALUE:
+ reg->id = 0;
+ break;
+ case PTR_TO_PACKET:
+ case PTR_TO_PACKET_META:
+ reg->id = ++env->id_gen;
+ break;
+ default:
+ break;
+ }
+}
+
+/* Check that ANY leaves can be unioned with r's loop-entry value. */
+static bool is_widenable_any(struct bpf_verifier_env *env, struct bpf_func_state *loop_entry,
+ struct env *header_env, u32 r, u32 id)
+{
+ const struct bpf_reg_state *reg, *leaf_reg;
+ struct scev *scev = env->scev;
+ u32 l, ra, rr, leaf, order;
+ s64 imm;
+
+ if (!is_any(scev, id, &l, &rr))
+ return false;
+ reg = scev_reg_for_read(env, loop_entry, r);
+ if (!reg || !is_widenable_reg_type(reg))
+ return false;
+
+ scev->stack_sz = 0;
+ expr_stack_push(scev, id);
+ while (expr_next(scev, &id, &order)) {
+ if (order & DEPTH_LIMIT)
+ return false;
+ if (!(order & PRE) || is_any(scev, id, &l, &rr))
+ continue;
+ if (is_imm(scev, id, &imm)) {
+ if (reg->type != SCALAR_VALUE)
+ return false;
+ } else if (is_reg(scev, id, &leaf)) {
+ /* Other leaves must denote loop-invariant registers. */
+ if (leaf != r &&
+ !(is_reg(scev, header_env->reg2scev[leaf], &ra) && ra == leaf))
+ return false;
+ leaf_reg = scev_reg_for_read(env, loop_entry, leaf);
+ if (!leaf_reg || !bpf_reg_union_compatible(reg, leaf_reg))
+ return false;
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+struct bounds {
+ struct cnum64 r64;
+ struct cnum32 r32;
+ u16 base;
+ u16 step;
+};
+
+static void log_bounds(struct bpf_verifier_env *env, const struct bounds *bounds)
+{
+ struct bpf_verifier_log *log = &env->log;
+
+ bpf_verbose_snum(env, cnum64_smin(bounds->r64));
+ bpf_log(log, "..");
+ bpf_verbose_snum(env, cnum64_smax(bounds->r64));
+ if (bounds->r32.size < U32_MAX) {
+ bpf_log(log, " (s32 ");
+ bpf_verbose_snum(env, cnum32_smin(bounds->r32));
+ bpf_log(log, "..");
+ bpf_verbose_snum(env, cnum32_smax(bounds->r32));
+ bpf_log(log, ")");
+ }
+ bpf_log(log, " step %u", bounds->step);
+}
+
+struct linear_coefs {
+ u32 base_reg;
+ s64 slope;
+ bool zext32;
+};
+
+static bool is_simple_linear(struct scev *scev, u32 id, struct linear_coefs *out)
+{
+ u32 base, slope;
+
+ return is_linear_or_linear32(scev, id, &base, &slope, &out->zext32) &&
+ is_reg(scev, base, &out->base_reg) &&
+ is_imm(scev, slope, &out->slope) &&
+ out->slope <= S16_MAX &&
+ out->slope >= S16_MIN &&
+ out->slope != 0;
+}
+
+static struct cnum64 cnum64_zext32(struct cnum32 r32)
+{
+ return cnum64_from_urange(cnum32_umin(r32), cnum32_umax(r32));
+}
+
+/* Compute the range an induction variable in `reg` spans over the loop. */
+static void linear_bounds(struct bpf_reg_state *reg, u32 max_header_count,
+ struct linear_coefs *coefs, struct bounds *out)
+{
+ s64 slope = coefs->slope;
+ s64 slope_abs = slope < 0 ? -slope : slope;
+ s64 min_val = reg_smin(reg);
+ s64 max_val = reg_smax(reg);
+ struct cnum64 r64;
+ struct cnum32 r32;
+ s64 total_change;
+ bool overflow;
+
+ overflow = check_mul_overflow(slope, (s64)max_header_count - 1, &total_change) ||
+ (slope > 0 && check_add_overflow(max_val, total_change, &max_val)) ||
+ (slope < 0 && check_add_overflow(min_val, total_change, &min_val));
+ r64 = overflow ? CNUM64_UNBOUNDED : cnum64_from_srange(min_val, max_val);
+ r32 = CNUM32_UNBOUNDED;
+ if (coefs->zext32) {
+ r32 = cnum32_from_cnum64(r64);
+ r64 = cnum64_zext32(r32);
+ /*
+ * 32-bit iteration overflow is equivalent to subtracting N * 2**32:
+ * (base + slope * k) % slope ≠ (base + slope * k - N * 2**32) % slope.
+ */
+ overflow |= cnum32_contains(r32, 0) && cnum32_contains(r32, U32_MAX);
+ /*
+ * The entry and zero-extended values must agree on how they define `base`,
+ * otherwise `base + slope * k` would define different sets of points between
+ * the two.
+ */
+ overflow |= imod(reg_smin(reg), slope_abs) !=
+ imod(cnum64_smin(r64), slope_abs);
+ /*
+ * Full range is a union of a reg's value at loop entry,
+ * plus a range produced by 32-bit iteration.
+ */
+ r64 = cnum64_union(reg->r64, r64);
+ r32 = cnum32_union(reg->r32, r32);
+ }
+ /*
+ * If the entry value is a single point the value set is 'v + slope * k',
+ * so the step is |slope|. Otherwise, only the power-of-two alignment
+ * shared by the entry value and the slope.
+ */
+ if (cnum64_is_const(reg->r64) && !overflow) {
+ out->step = slope_abs;
+ out->base = imod(reg_smin(reg), out->step);
+ } else {
+ out->step = 1u << min_t(u32, tnum_alignment(reg->var_off), __ffs(slope_abs));
+ out->base = 0;
+ }
+ out->r64 = r64;
+ out->r32 = r32;
+}
+
+static int mark_expr_precise(struct bpf_verifier_env *env, struct bpf_func_state *st, u32 id,
+ unsigned long *precise_regs)
+{
+ struct scev *scev = env->scev;
+ struct bpf_reg_state *reg;
+ u32 r, order;
+
+ scev->stack_sz = 0;
+ expr_stack_push(scev, id);
+ while (expr_next(scev, &id, &order)) {
+ if (order & DEPTH_LIMIT) {
+ verifier_bug(env, "scev precision marking exceeds expression depth limit");
+ return -EFAULT;
+ }
+ if (!(order & PRE) || !is_reg(scev, id, &r))
+ continue;
+ /* Synthetic values have no spilled register to mark precise. */
+ reg = scev_reg_for_linear(st, r);
+ if (reg && reg->type == SCALAR_VALUE)
+ __set_bit(r, precise_regs);
+ }
+ return 0;
+}
+
+int bpf_compute_loop_iters(struct bpf_verifier_env *env, struct bpf_verifier_state *st,
+ struct bpf_loop_iters *iters)
+{
+ struct bpf_func_state *cur_func = st->frame[st->curframe];
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_verifier_log *log = &env->log;
+ struct scev *scev = env->scev;
+ struct env *header_env;
+ struct linear_latch latch, best;
+ struct linear_coefs coefs;
+ struct bpf_backedge *backedge;
+ struct bpf_loop_iters tmp;
+ struct bpf_loop *loop;
+ int insn_idx = st->insn_idx;
+ bool found = false;
+ int linear_latch;
+ int latch_idx;
+ int err, j;
+ u32 r;
+
+ if (!env->allow_uninit_stack)
+ return 0;
+
+ /*
+ * If insn_idx is a loop header for a reducible loop with a single backedge.
+ * loop is NULL for secondary entries to irreducible loops.
+ */
+ loop = aux[insn_idx].loop;
+ if (!loop || loop->irreducible || loop->backedges_cnt != 1 || loop->backedges_overflow ||
+ loop->exits_overflow) {
+ if (log->level & BPF_LOG_LEVEL2)
+ bpf_log(log, "loop header at %d, unsupported loop:%s%s%s\n", insn_idx,
+ !loop || loop->irreducible ? " irreducible" : "",
+ loop && loop->backedges_cnt > 1 ? " multiple backedges" : "",
+ loop && loop->exits_overflow ? " too many exits" : "");
+ return 0;
+ }
+
+ /* If this backedge has a latch */
+ backedge = &loop->backedges[0];
+ if (!backedge->latches_cnt) {
+ if (log->level & BPF_LOG_LEVEL2)
+ bpf_log(log, "loop header at %d, latch not identified\n", insn_idx);
+ return 0;
+ }
+
+ /*
+ * Every condition dominating the backedge bounds the number of
+ * iterations from above, keep the smallest bound.
+ */
+ for (j = 0; j < backedge->latches_cnt; j++) {
+ latch_idx = backedge->latches[j];
+ linear_latch = match_linear_latch(env, insn_idx, latch_idx, &latch);
+ if (linear_latch < 0)
+ return linear_latch;
+ if (!linear_latch) {
+ if (log->level & BPF_LOG_LEVEL2)
+ bpf_log(log, "loop header at %d, non-linear %s at %d\n",
+ insn_idx, j ? "exit" : "latch", latch_idx);
+ continue;
+ }
+ if (!compute_max_iters(env, cur_func, &latch, &tmp)) {
+ if (log->level & BPF_LOG_LEVEL2)
+ bpf_log(log,
+ "loop header at %d, can't compute iterations count at %d\n",
+ insn_idx, latch_idx);
+ continue;
+ }
+ if (!found || tmp.max_header_count < iters->max_header_count) {
+ iters->max_header_count = tmp.max_header_count;
+ best = latch;
+ }
+ iters->min_header_count = found ? min(iters->min_header_count, tmp.min_header_count)
+ : tmp.min_header_count;
+ found = true;
+ }
+ if (!found) {
+ if (log->level & BPF_LOG_LEVEL2)
+ bpf_log(log, "loop header at %d, can't compute iterations count\n", insn_idx);
+ return 0;
+ }
+ latch = best;
+
+ if (iters->max_header_count == 0) {
+ if (log->level & BPF_LOG_LEVEL2)
+ bpf_log(log, "loop header at %d, 0 iterations count\n", insn_idx);
+ return 0;
+ }
+
+ if (iters->max_header_count == U32_MAX) {
+ if (log->level & BPF_LOG_LEVEL2)
+ bpf_log(log, "loop header at %d, inf iterations count\n", insn_idx);
+ return 0;
+ }
+
+ if (log->level & BPF_LOG_LEVEL2) {
+ bpf_log(log, "loop header at %d, header_count is ", insn_idx);
+ if (iters->min_header_count == iters->max_header_count)
+ bpf_log(log, "%u ", iters->max_header_count);
+ else
+ bpf_log(log, "[%u..%u] ", iters->min_header_count, iters->max_header_count);
+ bpf_log(log, "\n");
+ }
+
+ err = bpf_live_stack_query_init(env, st);
+ if (err)
+ return err;
+
+ header_env = find_header_env(scev, insn_idx);
+ for (r = 0; r < REGS_NUM; r++) {
+ struct bpf_reg_state *reg;
+ u32 ra, r_scev, r_expr;
+ bool spill_base = false;
+
+ if (!scev_reg_alive(env, st, r))
+ continue;
+ /*
+ * A heuristic:
+ * Ignore SCEVs computed for unbound values and treat those as widened.
+ * This is poor man's modelling of e.g. bpf_probe_read_kernel() effect
+ * on stack, when called from a subprogram. Currently this would produce
+ * an unknown ('?') SCEV as a summary of a subprogram call effects.
+ * Ideally, this should be computed as an OPAQUE value.
+ * However, that would require tracking STACK_MISC writes in the
+ * liveness analysis. Not implemented yet.
+ */
+ if (is_unbound_scev_reg(env, cur_func, r))
+ continue;
+
+ r_scev = header_env->reg2scev[r];
+ reg = scev_reg_for_linear(cur_func, r);
+ /* If SCEV for r is (linear <reg> <slope>) */
+ if (reg && is_simple_linear(scev, r_scev, &coefs) && is_widenable_reg_type(reg)) {
+ /* Spills at varying offsets lose precision */
+ if (test_bit(r, loop->store_base_regs)) {
+ spill_base = true;
+ goto cant_widen;
+ }
+ continue;
+ }
+
+ /* rA = rA, loop does not change this reg */
+ if (is_reg(scev, r_scev, &ra) && r == ra)
+ continue;
+
+ /* (any 1 (any 2 (any 3 4))) */
+ if (is_widenable_any(env, cur_func, header_env, r, r_scev)) {
+ if (test_bit(r, loop->store_base_regs)) {
+ spill_base = true;
+ goto cant_widen;
+ }
+ continue;
+ }
+cant_widen:
+ if (log->level & BPF_LOG_LEVEL2) {
+ r_expr = header_env->reg2expr[r];
+ bpf_log(log, "loop header at %d, can't widen ", insn_idx);
+ log_reg(env, r);
+ bpf_log(log, ", expr is ");
+ log_expr(env, r_expr);
+ if (spill_base)
+ bpf_log(log, ", requires exact stack-offset tracking");
+ bpf_log(log, "\n");
+ }
+ return 0;
+ }
+
+ bitmap_zero(iters->precise_regs, REGS_NUM);
+ err = mark_expr_precise(env, cur_func, latch.base_expr, iters->precise_regs);
+ if (err)
+ return err;
+ err = mark_expr_precise(env, cur_func, latch.step_expr, iters->precise_regs);
+ if (err)
+ return err;
+ err = mark_expr_precise(env, cur_func, latch.bound_expr, iters->precise_regs);
+ if (err)
+ return err;
+
+ return 1;
+}
+
+/* bpf_compute_loop_iters() checked that every leaf can be unioned into acc. */
+static int union_any_reg(struct bpf_verifier_env *env, struct bpf_func_state *loop_entry,
+ struct bpf_reg_state *acc, u32 id)
+{
+ struct bpf_reg_state *tmp = &env->fake_reg[0];
+ const struct bpf_reg_state *leaf_reg;
+ struct scev *scev = env->scev;
+ u32 l, r, leaf, order;
+ s64 imm;
+ int err;
+
+ scev->stack_sz = 0;
+ expr_stack_push(scev, id);
+ while (expr_next(scev, &id, &order)) {
+ if (order & DEPTH_LIMIT) {
+ verifier_bug(env, "scev ANY union exceeds expression depth limit");
+ return -EFAULT;
+ }
+ if (!(order & PRE) || is_any(scev, id, &l, &r))
+ continue;
+ if (is_imm(scev, id, &imm)) {
+ bpf_mark_reg_known_scalar(tmp, imm);
+ leaf_reg = tmp;
+ } else if (is_reg(scev, id, &leaf)) {
+ leaf_reg = scev_reg_for_read(env, loop_entry, leaf);
+ if (verifier_bug_if(!leaf_reg, env, "unreadable SCEV ANY leaf %u", leaf))
+ return -EFAULT;
+ } else {
+ verifier_bug(env, "scev ANY union has an unsupported leaf");
+ return -EFAULT;
+ }
+ err = bpf_reg_union(env, acc, leaf_reg);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+int bpf_widen_scev_regs(struct bpf_verifier_env *env, struct bpf_verifier_state *st,
+ struct bpf_verifier_state *loop_entry, struct bpf_loop_iters *iters)
+{
+ struct bpf_func_state *cur_func = st->frame[st->curframe];
+ struct bpf_func_state *entry_func = loop_entry->frame[loop_entry->curframe];
+ struct bpf_verifier_log *log = &env->log;
+ struct scev *scev = env->scev;
+ struct bpf_reg_state *reg;
+ struct env *header_env;
+ struct linear_coefs coefs;
+ struct bounds bounds;
+ u32 r, r_expr, a, b;
+ int insn_idx = st->insn_idx;
+ int err;
+
+ for_each_set_bit(r, iters->precise_regs, REGS_NUM) {
+ if (r < MAX_BPF_REG)
+ bpf_bt_set_frame_reg(&env->bt, st->curframe, r);
+ else
+ bpf_bt_set_frame_slot(&env->bt, st->curframe, r - MAX_BPF_REG);
+ }
+ err = bpf_mark_chain_precision(env, st, -1, NULL);
+ if (err)
+ return err;
+
+ header_env = find_header_env(scev, insn_idx);
+ for (r = 0; r < REGS_NUM; r++) {
+ if (!scev_reg_alive(env, st, r) || is_unbound_scev_reg(env, cur_func, r))
+ continue;
+
+ r_expr = header_env->reg2scev[r];
+ /* If SCEV for r is (linear <reg> <slope>)*/
+ if (is_simple_linear(scev, r_expr, &coefs) && coefs.base_reg == r) {
+ err = scev_reg_for_write(env, cur_func, r, &reg);
+ if (err)
+ return err;
+ linear_bounds(reg, iters->max_header_count, &coefs, &bounds);
+ if (log->level & BPF_LOG_LEVEL2) {
+ bpf_log(log, "loop header at %d, widening ", insn_idx);
+ log_reg(env, r);
+ bpf_log(log, " to ");
+ log_bounds(env, &bounds);
+ bpf_log(log, "\n");
+ }
+ scratch_widened_reg_id(env, reg);
+ err = bpf_set_reg_range(env, reg, bounds.r64, bounds.r32,
+ bounds.base, bounds.step);
+ if (err)
+ return err;
+ mark_scev_reg_scratched(env, r);
+ } else if (is_any(scev, r_expr, &a, &b)) {
+ err = scev_reg_for_write(env, cur_func, r, &reg);
+ if (err)
+ return err;
+ err = union_any_reg(env, entry_func, reg, r_expr);
+ if (err)
+ return err;
+ if (log->level & BPF_LOG_LEVEL2) {
+ bpf_log(log, "loop header at %d, widening ", insn_idx);
+ log_reg(env, r);
+ bpf_log(log, " to ");
+ bpf_verbose_snum(env, reg_smin(reg));
+ bpf_log(log, "..");
+ bpf_verbose_snum(env, reg_smax(reg));
+ bpf_log(log, " step %u\n", reg->step);
+ }
+ scratch_widened_reg_id(env, reg);
+ mark_scev_reg_scratched(env, r);
+ }
+ }
+ return 1;
+}
+
+int bpf_clamp_scev_regs(struct bpf_verifier_env *env, struct bpf_func_state *cur_func_state, u32 insn_idx,
+ struct bpf_verifier_state *entry_state, u32 max_header_count)
+{
+ struct bpf_func_state *entry_st = entry_state->frame[entry_state->curframe];
+ struct bpf_verifier_log *log = &env->log;
+ struct bpf_reg_state *reg, *entry_reg;
+ struct scev *scev = env->scev;
+ struct linear_coefs coefs;
+ struct env *header_env;
+ struct bounds bounds;
+ u32 r;
+ int err;
+
+ if (entry_state->curframe != cur_func_state->frameno) {
+ verifier_bug(env, "clamping registers for a wrong frame: %d vs %d\n",
+ entry_state->curframe, cur_func_state->frameno);
+ return -EFAULT;
+ }
+
+ header_env = find_header_env(scev, insn_idx);
+ for (r = 0; r < REGS_NUM; r++) {
+ /* If SCEV for r is (linear <reg> <slope>)*/
+ if (!is_simple_linear(scev, header_env->reg2scev[r], &coefs) ||
+ coefs.base_reg != r)
+ continue;
+
+ entry_reg = scev_reg_for_linear(entry_st, r);
+ reg = scev_reg_for_linear(cur_func_state, r);
+ if (!entry_reg || !reg || entry_reg->type == NOT_INIT || reg->type == NOT_INIT ||
+ is_unbound_scev_reg(env, entry_st, r))
+ continue;
+
+ linear_bounds(entry_reg, max_header_count, &coefs, &bounds);
+ bounds.r64 = cnum64_intersect(reg->r64, bounds.r64);
+ bounds.r32 = cnum32_intersect(reg->r32, bounds.r32);
+ if (cnum64_is_empty(bounds.r64)) {
+ verifier_bug(env, "scev clamp produced empty range for r%d", r);
+ return -EFAULT;
+ }
+ if (log->level & BPF_LOG_LEVEL2) {
+ bpf_log(log, "loop header at %d, clamping ", insn_idx);
+ log_reg(env, r);
+ bpf_log(log, " to ");
+ log_bounds(env, &bounds);
+ bpf_log(log, "\n");
+ }
+ scratch_widened_reg_id(env, reg);
+ err = bpf_set_reg_range(env, reg, bounds.r64, bounds.r32,
+ bounds.base, bounds.step);
+ if (err)
+ return err;
+ mark_scev_reg_scratched(env, r);
+ }
+ return 0;
+}
diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c
index 012b82513a3b..d7df6632a5c4 100644
--- a/kernel/bpf/states.c
+++ b/kernel/bpf/states.c
@@ -302,8 +302,30 @@ int bpf_update_branch_counts(struct bpf_verifier_env *env, struct bpf_verifier_s
static bool range_within(const struct bpf_reg_state *old,
const struct bpf_reg_state *cur)
{
- return cnum64_is_subset(old->r64, cur->r64) &&
- cnum32_is_subset(old->r32, cur->r32);
+ if (!cnum64_is_subset(old->r64, cur->r64) ||
+ !cnum32_is_subset(old->r32, cur->r32))
+ return false;
+
+ if (old->step <= 1)
+ return true;
+
+ if (cnum64_is_const(cur->r64))
+ return imod((s64)cnum64_smin(cur->r64), old->step) == old->base;
+
+ /*
+ * Both `old` and `cur` define some sets of points.
+ * Return true, if points defined by `cur` are a subset of points defined by `old`:
+ * - bounds for `cur` should be within bounds for `old`;
+ * - cur->step should be dividable by old->step;
+ * - cur->base should start at integer number of old->step
+ * steps from old->base.
+ *
+ * E.g. the following ranges are compatible:
+ * - old [0, 10] step 2
+ * - new [2, 10] step 4
+ */
+ return cur->step % old->step == 0 &&
+ (cur->base - old->base) % old->step == 0;
}
/* If in the old state two registers had the same id, then they need to have
@@ -415,14 +437,14 @@ static void __clean_func_state(struct bpf_verifier_env *env,
* half_spi 2*i → lower half: slot_type[0..3] (closer to FP)
* half_spi 2*i+1 → upper half: slot_type[4..7] (farther from FP)
*/
- for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) {
+ for (i = 0; i < bpf_stack_nr_slots(st); i++) {
bool lo_live = bpf_stack_slot_alive(env, frame, i * 2);
bool hi_live = bpf_stack_slot_alive(env, frame, i * 2 + 1);
if (!hi_live || !lo_live) {
int start = !lo_live ? 0 : BPF_REG_SIZE / 2;
int end = !hi_live ? BPF_REG_SIZE : BPF_REG_SIZE / 2;
- u8 stype = st->stack[i].slot_type[7];
+ u8 stype = bpf_stack_slot(st, i)->slot_type[7];
/*
* Don't clear special slots.
@@ -442,7 +464,7 @@ static void __clean_func_state(struct bpf_verifier_env *env,
* rejecting as non-scalar register fills.
*/
if (!hi_live) {
- struct bpf_reg_state *spill = &st->stack[i].spilled_ptr;
+ struct bpf_reg_state *spill = &bpf_stack_slot(st, i)->spilled_ptr;
if (lo_live && stype == STACK_SPILL) {
if (spill->type != SCALAR_VALUE)
@@ -454,7 +476,7 @@ static void __clean_func_state(struct bpf_verifier_env *env,
if (bpf_register_is_null(spill))
continue;
for (j = 0; j < 4; j++) {
- u8 *t = &st->stack[i].slot_type[j];
+ u8 *t = &bpf_stack_slot(st, i)->slot_type[j];
if (*t == STACK_SPILL)
*t = STACK_MISC;
@@ -463,7 +485,7 @@ static void __clean_func_state(struct bpf_verifier_env *env,
bpf_mark_reg_not_init(env, spill);
}
for (j = start; j < end; j++)
- st->stack[i].slot_type[j] = STACK_POISON;
+ bpf_stack_slot(st, i)->slot_type[j] = STACK_POISON;
}
}
}
@@ -610,6 +632,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
case PTR_TO_MEM:
case PTR_TO_BUF:
case PTR_TO_TP_BUFFER:
+ case PTR_TO_STACK:
+ case PTR_TO_BTF_ID:
/* If the new min/max/var_off satisfy the old ones and
* everything else matches, we are OK.
*/
@@ -643,8 +667,6 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
/* new val must satisfy old val knowledge */
return range_within(rold, rcur) &&
tnum_in(rold->var_off, rcur->var_off);
- case PTR_TO_STACK:
- return regs_exact(rold, rcur, idmap);
case PTR_TO_ARENA:
return true;
case PTR_TO_INSN:
@@ -709,37 +731,38 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
* didn't use them
*/
for (i = 0; i < old->allocated_stack; i++) {
+ struct bpf_stack_state *old_slot, *cur_slot;
struct bpf_reg_state *old_reg, *cur_reg;
int im = i % BPF_REG_SIZE;
+ u8 old_type;
spi = i / BPF_REG_SIZE;
+ old_slot = bpf_stack_slot(old, spi);
+ old_type = old_slot->slot_type[im];
+ cur_slot = i < cur->allocated_stack ? bpf_stack_slot(cur, spi) : NULL;
if (exact == EXACT) {
- u8 old_type = old->stack[spi].slot_type[i % BPF_REG_SIZE];
- u8 cur_type = i < cur->allocated_stack ?
- cur->stack[spi].slot_type[i % BPF_REG_SIZE] : STACK_INVALID;
+ u8 cur_type = cur_slot ? cur_slot->slot_type[im] : STACK_INVALID;
/* STACK_INVALID and STACK_POISON are equivalent for pruning */
if (old_type == STACK_POISON)
old_type = STACK_INVALID;
if (cur_type == STACK_POISON)
cur_type = STACK_INVALID;
- if (i >= cur->allocated_stack || old_type != cur_type)
+ if (!cur_slot || old_type != cur_type)
return false;
}
- if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID ||
- old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_POISON)
+ if (old_type == STACK_INVALID || old_type == STACK_POISON)
continue;
- if (env->allow_uninit_stack &&
- old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_MISC)
+ if (env->allow_uninit_stack && old_type == STACK_MISC)
continue;
/* explored stack has more populated slots than current stack
* and these slots were used
*/
- if (i >= cur->allocated_stack)
+ if (!cur_slot)
return false;
/*
@@ -749,8 +772,8 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
* regsafe() to ensure scalar ids are compared.
*/
if (im == 0 || im == 4) {
- old_reg = scalar_reg_for_stack(env, &old->stack[spi], im);
- cur_reg = scalar_reg_for_stack(env, &cur->stack[spi], im);
+ old_reg = scalar_reg_for_stack(env, old_slot, im);
+ cur_reg = scalar_reg_for_stack(env, cur_slot, im);
if (old_reg && cur_reg) {
if (!regsafe(env, old_reg, cur_reg, idmap, exact))
return false;
@@ -763,21 +786,19 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
* it will be safe with zero-initialized stack.
* The opposite is not true
*/
- if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_MISC &&
- cur->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_ZERO)
+ if (old_type == STACK_MISC && cur_slot->slot_type[im] == STACK_ZERO)
continue;
- if (old->stack[spi].slot_type[i % BPF_REG_SIZE] !=
- cur->stack[spi].slot_type[i % BPF_REG_SIZE])
+ if (old_type != cur_slot->slot_type[im])
/* Ex: old explored (safe) state has STACK_SPILL in
* this stack slot, but current has STACK_MISC ->
* this verifier states are not equivalent,
* return false to continue verification of this path
*/
return false;
- if (i % BPF_REG_SIZE != BPF_REG_SIZE - 1)
+ if (im != BPF_REG_SIZE - 1)
continue;
/* Both old and cur are having same slot_type */
- switch (old->stack[spi].slot_type[BPF_REG_SIZE - 1]) {
+ switch (old_type) {
case STACK_SPILL:
/* when explored and current stack slot are both storing
* spilled registers, check that stored pointers types
@@ -789,13 +810,13 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
* such verifier states are not equivalent.
* return false to continue verification of this path
*/
- if (!regsafe(env, &old->stack[spi].spilled_ptr,
- &cur->stack[spi].spilled_ptr, idmap, exact))
+ if (!regsafe(env, &old_slot->spilled_ptr, &cur_slot->spilled_ptr,
+ idmap, exact))
return false;
break;
case STACK_DYNPTR:
- old_reg = &old->stack[spi].spilled_ptr;
- cur_reg = &cur->stack[spi].spilled_ptr;
+ old_reg = &old_slot->spilled_ptr;
+ cur_reg = &cur_slot->spilled_ptr;
if (old_reg->dynptr.type != cur_reg->dynptr.type ||
old_reg->dynptr.first_slot != cur_reg->dynptr.first_slot ||
!check_ids(old_reg->id, cur_reg->id, idmap) ||
@@ -803,8 +824,8 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
return false;
break;
case STACK_ITER:
- old_reg = &old->stack[spi].spilled_ptr;
- cur_reg = &cur->stack[spi].spilled_ptr;
+ old_reg = &old_slot->spilled_ptr;
+ cur_reg = &cur_slot->spilled_ptr;
/* iter.depth is not compared between states as it
* doesn't matter for correctness and would otherwise
* prevent convergence; we maintain it only to prevent
@@ -820,8 +841,8 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
return false;
break;
case STACK_IRQ_FLAG:
- old_reg = &old->stack[spi].spilled_ptr;
- cur_reg = &cur->stack[spi].spilled_ptr;
+ old_reg = &old_slot->spilled_ptr;
+ cur_reg = &cur_slot->spilled_ptr;
if (!check_ids(old_reg->id, cur_reg->id, idmap) ||
old_reg->irq.kfunc_class != cur_reg->irq.kfunc_class)
return false;
@@ -915,6 +936,27 @@ static bool refsafe(struct bpf_verifier_state *old, struct bpf_verifier_state *c
return true;
}
+static bool loop_stack_safe(struct bpf_verifier_env *env, struct bpf_func_state *old,
+ struct bpf_func_state *cur)
+{
+ struct loop_stack_entry *old_stack = old->loop_stack;
+ struct loop_stack_entry *cur_stack = cur->loop_stack;
+ u32 i;
+
+ if (old->loop_stack_cnt != cur->loop_stack_cnt)
+ return false;
+
+ for (i = 0; i < old->loop_stack_cnt; i++) {
+ if (old_stack[i].loop_id == cur_stack[i].loop_id &&
+ old_stack[i].max_header_count == cur_stack[i].max_header_count &&
+ old_stack[i].terminates == cur_stack[i].terminates)
+ continue;
+ return false;
+ }
+
+ return true;
+}
+
/* compare two verifier states
*
* all states stored in state_list are known to be valid, since
@@ -953,6 +995,9 @@ static bool func_states_equal(struct bpf_verifier_env *env, struct bpf_func_stat
if (!old->no_stack_arg_load && cur->no_stack_arg_load)
return false;
+ if (old->in_rcu_callback_fn != cur->in_rcu_callback_fn)
+ return false;
+
for (i = 0; i < MAX_BPF_REG; i++)
if (((1 << i) & live_regs) &&
!regsafe(env, &old->regs[i], &cur->regs[i],
@@ -965,6 +1010,9 @@ static bool func_states_equal(struct bpf_verifier_env *env, struct bpf_func_stat
if (!stack_arg_safe(env, old, cur, &env->idmap_scratch, exact))
return false;
+ if (!loop_stack_safe(env, old, cur))
+ return false;
+
return true;
}
@@ -998,6 +1046,9 @@ static bool states_equal(struct bpf_verifier_env *env,
if (old->in_sleepable != cur->in_sleepable)
return false;
+ if (old->unwound != cur->unwound)
+ return false;
+
if (!refsafe(old, cur, &env->idmap_scratch))
return false;
@@ -1011,6 +1062,7 @@ static bool states_equal(struct bpf_verifier_env *env,
if (!func_states_equal(env, old->frame[i], cur->frame[i], insn_idx, exact))
return false;
}
+
return true;
}
@@ -1045,10 +1097,12 @@ static int propagate_precision(struct bpf_verifier_env *env,
first = false;
}
- for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
- if (!bpf_is_spilled_reg(&state->stack[i]))
+ for (i = 0; i < bpf_stack_nr_slots(state); i++) {
+ struct bpf_stack_state *ss = bpf_stack_slot(state, i);
+
+ if (!bpf_is_spilled_reg(ss))
continue;
- state_reg = &state->stack[i].spilled_ptr;
+ state_reg = &ss->spilled_ptr;
if (state_reg->type != SCALAR_VALUE ||
!state_reg->precise)
continue;
@@ -1123,7 +1177,7 @@ static bool states_maybe_looping(struct bpf_verifier_state *old,
fcur = cur->frame[fr];
for (i = 0; i < MAX_BPF_REG; i++)
if (memcmp(&fold->regs[i], &fcur->regs[i],
- offsetof(struct bpf_reg_state, precise)))
+ offsetofend(struct bpf_reg_state, step)))
return false;
return true;
}
@@ -1194,15 +1248,17 @@ static bool iter_active_depths_differ(struct bpf_verifier_state *old, struct bpf
for (fr = old->curframe; fr >= 0; fr--) {
state = old->frame[fr];
- for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
- if (state->stack[i].slot_type[0] != STACK_ITER)
+ for (i = 0; i < bpf_stack_nr_slots(state); i++) {
+ struct bpf_stack_state *ss = bpf_stack_slot(state, i);
+
+ if (ss->slot_type[0] != STACK_ITER)
continue;
- slot = &state->stack[i].spilled_ptr;
+ slot = &ss->spilled_ptr;
if (slot->iter.state != BPF_ITER_STATE_ACTIVE)
continue;
- cur_slot = &cur->frame[fr]->stack[i].spilled_ptr;
+ cur_slot = &bpf_stack_slot(cur->frame[fr], i)->spilled_ptr;
if (cur_slot->iter.depth != slot->iter.depth)
return true;
}
@@ -1224,10 +1280,12 @@ static void mark_all_scalars_imprecise(struct bpf_verifier_env *env, struct bpf_
continue;
reg->precise = false;
}
- for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) {
- if (!bpf_is_spilled_reg(&func->stack[j]))
+ for (j = 0; j < bpf_stack_nr_slots(func); j++) {
+ struct bpf_stack_state *ss = bpf_stack_slot(func, j);
+
+ if (!bpf_is_spilled_reg(ss))
continue;
- reg = &func->stack[j].spilled_ptr;
+ reg = &ss->spilled_ptr;
if (reg->type != SCALAR_VALUE)
continue;
reg->precise = false;
@@ -1235,16 +1293,92 @@ static void mark_all_scalars_imprecise(struct bpf_verifier_env *env, struct bpf_
}
}
-int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
+int bpf_split_cur_state(struct bpf_verifier_env *env)
{
+ struct bpf_verifier_state *cur = env->cur_state, *new;
struct bpf_verifier_state_list *new_sl;
+ struct list_head *head;
+ int insn_idx = cur->insn_idx;
+ int err;
+
+ head = bpf_explored_state(env, insn_idx);
+ new_sl = kzalloc_obj(struct bpf_verifier_state_list, GFP_KERNEL_ACCOUNT);
+ if (!new_sl)
+ return -ENOMEM;
+ env->total_states++;
+ env->explored_states_size++;
+ update_peak_states(env);
+ env->prev_jmps_processed = env->jmps_processed;
+ env->prev_insn_processed = env->insn_processed;
+
+ /* forget precise markings we inherited, see __mark_chain_precision */
+ if (env->bpf_capable)
+ mark_all_scalars_imprecise(env, cur);
+
+ bpf_clear_singular_ids(env, cur);
+
+ /* add new state to the head of linked list */
+ new = &new_sl->state;
+ err = bpf_copy_verifier_state(new, cur);
+ if (err) {
+ bpf_free_verifier_state(new, false);
+ kfree(new_sl);
+ return err;
+ }
+ new->insn_idx = insn_idx;
+ verifier_bug_if(new->branches != 1, env,
+ "%s:branches_to_explore=%d insn %d",
+ __func__, new->branches, insn_idx);
+ err = maybe_enter_scc(env, new);
+ if (err) {
+ bpf_free_verifier_state(new, false);
+ kfree(new_sl);
+ return err;
+ }
+
+ cur->parent = new;
+ cur->last_insn_idx = -1;
+ cur->first_insn_idx = insn_idx;
+ cur->dfs_depth = new->dfs_depth + 1;
+ bpf_clear_jmp_history(cur);
+ list_add(&new_sl->node, head);
+ return 0;
+}
+
+/* Force a checkpoint upon reaching a loop header for a terminating loop. */
+static bool need_loop_checkpoint(struct bpf_verifier_env *env, int insn_idx)
+{
+ struct bpf_verifier_state *cur = env->cur_state;
+ struct bpf_func_state *frame = cur->frame[cur->curframe];
+ struct loop_stack_entry *top;
+
+ if (bpf_loop_at_index(env, insn_idx) != insn_idx || !frame->loop_stack_cnt)
+ return false;
+ top = &frame->loop_stack[frame->loop_stack_cnt - 1];
+ return top->loop_id == insn_idx && top->terminates;
+}
+
+static struct loop_stack_entry *current_loop(struct bpf_verifier_state *st)
+{
+ struct bpf_func_state *frame = st->frame[st->curframe];
+
+ if (frame->loop_stack_cnt == 0)
+ return NULL;
+ return &frame->loop_stack[frame->loop_stack_cnt - 1];
+}
+
+int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
+{
+ struct loop_stack_entry *old_loop, *cur_loop;
struct bpf_verifier_state_list *sl;
- struct bpf_verifier_state *cur = env->cur_state, *new;
- bool force_new_state, add_new_state, loop;
+ struct bpf_verifier_state *cur = env->cur_state;
+ bool force_new_state, add_new_state, loop, loop_checkpoint;
int n, err, states_cnt = 0;
struct list_head *pos, *tmp, *head;
+ loop_checkpoint = need_loop_checkpoint(env, insn_idx);
force_new_state = env->test_state_freq || bpf_is_force_checkpoint(env, insn_idx) ||
+ loop_checkpoint ||
/* Avoid accumulating infinitely long jmp history */
cur->jmp_history_cnt > 40;
@@ -1275,10 +1409,10 @@ int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
continue;
if (sl->state.branches) {
- struct bpf_func_state *frame = sl->state.frame[sl->state.curframe];
+ struct bpf_func_state *old_top_frame = sl->state.frame[0];
- if (frame->in_async_callback_fn &&
- frame->async_entry_cnt != cur->frame[cur->curframe]->async_entry_cnt) {
+ if (old_top_frame->in_async_callback_fn &&
+ old_top_frame->async_entry_cnt != cur->frame[0]->async_entry_cnt) {
/* Different async_entry_cnt means that the verifier is
* processing another entry into async callback.
* Seeing the same state is not an indication of infinite
@@ -1330,8 +1464,9 @@ int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
*/
if (is_iter_next_insn(env, insn_idx)) {
if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) {
- struct bpf_func_state *cur_frame;
+ struct bpf_func_state *cur_frame, *iter_frame, *old_frame;
struct bpf_reg_state *iter_state, *iter_reg;
+ struct bpf_reg_state *old_iter;
int spi;
cur_frame = cur->frame[cur->curframe];
@@ -1344,8 +1479,18 @@ int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
* no need for extra (re-)validations
*/
spi = bpf_get_spi(iter_reg->var_off.value);
- iter_state = &bpf_func(env, iter_reg)->stack[spi].spilled_ptr;
- if (iter_state->iter.state == BPF_ITER_STATE_ACTIVE) {
+ iter_frame = bpf_func(env, iter_reg);
+ iter_state = &bpf_stack_slot(iter_frame, spi)->spilled_ptr;
+ old_frame = sl->state.frame[iter_reg->frameno];
+ old_iter = &bpf_stack_slot(old_frame, spi)->spilled_ptr;
+ /*
+ * states_equal() matches ids through idmap.
+ * The loop converged only if it's the same
+ * iterator. The one that was destroyed and
+ * created again starts from the beginning.
+ */
+ if (iter_state->iter.state == BPF_ITER_STATE_ACTIVE &&
+ iter_state->id == old_iter->id) {
loop = true;
goto hit;
}
@@ -1366,6 +1511,33 @@ int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
}
goto skip_inf_loop_check;
}
+ /*
+ * If old state belongs to a control flow loop that we know terminates,
+ * it should be safe to prune current state. However, account for the
+ * following situation:
+ *
+ * for (;;) {
+ * for (i = 0; i < 10; i++)
+ * ...
+ * }
+ *
+ * Here a backedge to a non-terminating outer loop is dominated by an exit
+ * from the inner loop. Pruning at a re-entry to the inner loop therefore
+ * would hide the fact that outer loop is non-terminating.
+ * Hence, only allow pruning states within the same entry state.
+ */
+ old_loop = current_loop(&sl->state);
+ cur_loop = current_loop(cur);
+ if (old_loop && cur_loop &&
+ old_loop->terminates && cur_loop->terminates &&
+ old_loop->entry_state == cur_loop->entry_state) {
+ if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) {
+ loop = true;
+ goto hit;
+ }
+ goto skip_inf_loop_check;
+ }
+
/* attempt to detect infinite loop to avoid unnecessary doomed work */
if (states_maybe_looping(&sl->state, cur) &&
states_equal(env, &sl->state, cur, EXACT) &&
@@ -1412,7 +1584,7 @@ hit:
*/
err = 0;
if (bpf_is_jmp_point(env, env->insn_idx))
- err = bpf_push_jmp_history(env, cur, 0, 0, 0, 0);
+ err = bpf_push_jmp_history(env, cur, 0, 0, 0, NULL, 0);
err = err ? : propagate_precision(env, &sl->state, cur, NULL);
if (err)
return err;
@@ -1524,7 +1696,8 @@ miss:
* Use bigger 'n' for checkpoints because evicting checkpoint states
* too early would hinder iterator convergence.
*/
- n = bpf_is_force_checkpoint(env, insn_idx) && sl->state.branches > 0 ? 64 : 3;
+ n = (bpf_is_force_checkpoint(env, insn_idx) || loop_checkpoint) &&
+ sl->state.branches > 0 ? 64 : 3;
if (sl->miss_cnt > sl->hit_cnt * n + n) {
/* the state is unlikely to be useful. Remove it to
* speed up verification
@@ -1556,44 +1729,5 @@ miss:
* When looping the sl->state.branches will be > 0 and this state
* will not be considered for equivalence until branches == 0.
*/
- new_sl = kzalloc_obj(struct bpf_verifier_state_list, GFP_KERNEL_ACCOUNT);
- if (!new_sl)
- return -ENOMEM;
- env->total_states++;
- env->explored_states_size++;
- update_peak_states(env);
- env->prev_jmps_processed = env->jmps_processed;
- env->prev_insn_processed = env->insn_processed;
-
- /* forget precise markings we inherited, see __mark_chain_precision */
- if (env->bpf_capable)
- mark_all_scalars_imprecise(env, cur);
-
- bpf_clear_singular_ids(env, cur);
-
- /* add new state to the head of linked list */
- new = &new_sl->state;
- err = bpf_copy_verifier_state(new, cur);
- if (err) {
- bpf_free_verifier_state(new, false);
- kfree(new_sl);
- return err;
- }
- new->insn_idx = insn_idx;
- verifier_bug_if(new->branches != 1, env,
- "%s:branches_to_explore=%d insn %d",
- __func__, new->branches, insn_idx);
- err = maybe_enter_scc(env, new);
- if (err) {
- bpf_free_verifier_state(new, false);
- kfree(new_sl);
- return err;
- }
-
- cur->parent = new;
- cur->first_insn_idx = insn_idx;
- cur->dfs_depth = new->dfs_depth + 1;
- bpf_clear_jmp_history(cur);
- list_add(&new_sl->node, head);
- return 0;
+ return bpf_split_cur_state(env);
}
diff --git a/kernel/bpf/stream.c b/kernel/bpf/stream.c
index be9ce98e9469..83e803ea54ef 100644
--- a/kernel/bpf/stream.c
+++ b/kernel/bpf/stream.c
@@ -2,11 +2,15 @@
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#include <linux/bpf.h>
+#include <linux/anon_inodes.h>
#include <linux/filter.h>
#include <linux/bpf_mem_alloc.h>
#include <linux/gfp.h>
+#include <linux/irq_work.h>
#include <linux/memory.h>
#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/refcount.h>
static void bpf_stream_elem_init(struct bpf_stream_elem *elem, int len)
{
@@ -22,11 +26,11 @@ static struct bpf_stream_elem *bpf_stream_elem_alloc(int len)
size_t alloc_size;
/*
- * Length denotes the amount of data to be written as part of stream element,
- * thus includes '\0' byte. We're capped by how much bpf_bprintf_buffers can
- * accomodate, therefore deny allocations that won't fit into them.
+ * Length is the payload pushed into the stream, excluding the
+ * trailing NUL of the bprintf buffer. Reject anything that cannot
+ * fit without copying that NUL into the stream element.
*/
- if (len < 0 || len > max_len)
+ if (len < 0 || len >= max_len)
return NULL;
alloc_size = offsetof(struct bpf_stream_elem, str[len]);
@@ -68,25 +72,82 @@ static int bpf_stream_consume_capacity(struct bpf_stream *stream, int len)
return 0;
}
-static void bpf_stream_release_capacity(struct bpf_stream *stream, struct bpf_stream_elem *elem)
+static void bpf_stream_release_capacity(struct bpf_stream *stream, int len)
{
- int len = elem->total_len;
-
atomic_sub(len, &stream->capacity);
}
+static void bpf_stream_notify(struct irq_work *work)
+{
+ struct bpf_stream *stream = container_of(work, struct bpf_stream, notify_work);
+
+ /*
+ * Writers run in arbitrary program contexts, including NMI and regions
+ * that already hold wait queue or epoll locks. Wake waiters from
+ * irq_work instead, where taking those locks is safe.
+ */
+ wake_up_interruptible_poll(&stream->waitq, EPOLLIN | EPOLLRDNORM);
+}
+
+static void bpf_stream_queue_notify(struct bpf_stream *stream)
+{
+ /*
+ * Record that the work has been used so that teardown only pays for
+ * irq_work_sync(), which may wait for an RCU grace period, when a
+ * callback could actually be in flight.
+ */
+ if (!READ_ONCE(stream->notify_used))
+ WRITE_ONCE(stream->notify_used, true);
+ irq_work_queue(&stream->notify_work);
+}
+
+static int bpf_stream_readable_bytes(struct bpf_stream *stream)
+{
+ return atomic_read_acquire(&stream->readable);
+}
+
+static void bpf_stream_publish(struct bpf_stream *stream, int len)
+{
+ /*
+ * Pairs with atomic_read_acquire() in bpf_stream_readable_bytes().
+ *
+ * Notify only when the stream turns from empty to readable. Readers
+ * block and pollers wait only after finding it empty, and a read never
+ * takes the count below zero, so the publication that makes it positive
+ * is the one they wait for. Publishing into a stream that already holds
+ * data would only raise an interrupt for waiters that are being woken
+ * already, or for nobody at all.
+ */
+ if (atomic_add_return_release(len, &stream->readable) == len)
+ bpf_stream_queue_notify(stream);
+}
+
static int bpf_stream_push_str(struct bpf_stream *stream, const char *str, int len)
{
- int ret = bpf_stream_consume_capacity(stream, len);
+ int ret;
+
+ /* Nothing to publish; do not allocate an element for it. */
+ if (!len)
+ return 0;
+
+ ret = bpf_stream_consume_capacity(stream, len);
+ if (ret)
+ return ret;
+
+ ret = __bpf_stream_push_str(&stream->log, str, len);
+ if (ret)
+ bpf_stream_release_capacity(stream, len);
+ else
+ bpf_stream_publish(stream, len);
- return ret ?: __bpf_stream_push_str(&stream->log, str, len);
+ return ret;
}
static struct bpf_stream *bpf_stream_get(enum bpf_stream_id stream_id, struct bpf_prog_aux *aux)
{
if (stream_id != BPF_STDOUT && stream_id != BPF_STDERR)
return NULL;
- return &aux->stream[stream_id - 1];
+ return aux->stream[stream_id - 1];
}
static void bpf_stream_free_elem(struct bpf_stream_elem *elem)
@@ -154,14 +215,17 @@ static bool bpf_stream_consume_elem(struct bpf_stream_elem *elem, int *len)
static int bpf_stream_read(struct bpf_stream *stream, void __user *buf, int len)
{
- int rem_len = len, cons_len, ret = 0;
+ int read_len, rem_len, cons_len, ret = 0;
struct bpf_stream_elem *elem = NULL;
struct llist_node *node;
mutex_lock(&stream->lock);
+ read_len = min(len, bpf_stream_readable_bytes(stream));
+ rem_len = read_len;
while (rem_len) {
- int pos = len - rem_len;
+ int pos = read_len - rem_len;
+ int chunk, n;
bool cont;
node = bpf_stream_backlog_peek(stream);
@@ -175,37 +239,146 @@ static int bpf_stream_read(struct bpf_stream *stream, void __user *buf, int len)
cons_len = elem->consumed_len;
cont = bpf_stream_consume_elem(elem, &rem_len) == false;
-
- ret = copy_to_user(buf + pos, elem->str + cons_len,
- elem->consumed_len - cons_len);
- /* Restore in case of error. */
- if (ret) {
- ret = -EFAULT;
- elem->consumed_len = cons_len;
+ chunk = elem->consumed_len - cons_len;
+
+ n = copy_to_user(buf + pos, elem->str + cons_len, chunk);
+ if (n) {
+ /* Keep any successfully copied bytes; -EFAULT only if none. */
+ elem->consumed_len -= n;
+ rem_len += n;
+ ret = (read_len == rem_len) ? -EFAULT : 0;
break;
}
if (cont)
continue;
bpf_stream_backlog_pop(stream);
- bpf_stream_release_capacity(stream, elem);
+ bpf_stream_release_capacity(stream, elem->total_len);
bpf_stream_free_elem(elem);
}
+ atomic_sub(read_len - rem_len, &stream->readable);
mutex_unlock(&stream->lock);
- return ret ? ret : len - rem_len;
+ return ret ? ret : read_len - rem_len;
}
-int bpf_prog_stream_read(struct bpf_prog *prog, enum bpf_stream_id stream_id, void __user *buf, int len)
+int bpf_prog_stream_read(struct bpf_prog *prog, enum bpf_stream_id stream_id, void __user *buf, u32 len)
{
struct bpf_stream *stream;
stream = bpf_stream_get(stream_id, prog->aux);
if (!stream)
return -ENOENT;
+ if (len > INT_MAX)
+ return -EINVAL;
return bpf_stream_read(stream, buf, len);
}
+static bool bpf_stream_has_data(struct bpf_stream *stream)
+{
+ return bpf_stream_readable_bytes(stream) > 0;
+}
+
+static void bpf_stream_put(struct bpf_stream *stream)
+{
+ if (refcount_dec_and_test(&stream->refcnt)) {
+ struct llist_node *list;
+
+ /* Only a stream that ever queued its work can have a callback in flight. */
+ if (READ_ONCE(stream->notify_used))
+ irq_work_sync(&stream->notify_work);
+ list = llist_del_all(&stream->log);
+ bpf_stream_free_list(list);
+ bpf_stream_free_list(stream->backlog_head);
+ mutex_destroy(&stream->lock);
+ kfree(stream);
+ }
+}
+
+static int bpf_stream_release(struct inode *inode, struct file *file)
+{
+ bpf_stream_put(file->private_data);
+ return 0;
+}
+
+static ssize_t bpf_stream_file_read(struct file *file, char __user *buf, size_t len,
+ loff_t *ppos)
+{
+ struct bpf_stream *stream = file->private_data;
+ bool dead;
+ int ret;
+
+ if (!len)
+ return 0;
+
+ for (;;) {
+ /*
+ * Sample teardown state before looking for data. Nothing is
+ * published once the program is gone, so finding the stream
+ * empty after observing dead means EOF. The opposite order could
+ * report EOF while data published just before teardown is still
+ * buffered.
+ */
+ dead = smp_load_acquire(&stream->dead);
+ ret = bpf_stream_read(stream, buf, len);
+ if (ret)
+ return ret;
+ if (dead)
+ return 0;
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(stream->waitq,
+ bpf_stream_has_data(stream) ||
+ READ_ONCE(stream->dead));
+ if (ret)
+ return ret;
+ }
+}
+
+static __poll_t bpf_stream_poll(struct file *file, struct poll_table_struct *pts)
+{
+ struct bpf_stream *stream = file->private_data;
+ __poll_t events = 0;
+
+ /*
+ * poll_wait() only registers the wait queue callback. Register before
+ * checking persistent state so a concurrent publication or teardown is
+ * observed either by the callback or by the checks below.
+ */
+ poll_wait(file, &stream->waitq, pts);
+ if (bpf_stream_has_data(stream))
+ events |= EPOLLIN | EPOLLRDNORM;
+ if (READ_ONCE(stream->dead))
+ events |= EPOLLHUP;
+ return events;
+}
+
+static const struct file_operations bpf_stream_fops = {
+ .release = bpf_stream_release,
+ .read = bpf_stream_file_read,
+ .poll = bpf_stream_poll,
+};
+
+int bpf_prog_stream_new_fd(struct bpf_prog *prog, enum bpf_stream_id stream_id, u32 flags)
+{
+ struct bpf_stream *stream;
+ int fd_flags = O_RDONLY | O_CLOEXEC;
+ int fd;
+
+ stream = bpf_stream_get(stream_id, prog->aux);
+ if (!stream)
+ return -ENOENT;
+ if (flags & BPF_F_STREAM_NONBLOCK)
+ fd_flags |= O_NONBLOCK;
+
+ refcount_inc(&stream->refcnt);
+ fd = anon_inode_getfd("bpf-stream", &bpf_stream_fops, stream, fd_flags);
+ if (fd < 0)
+ bpf_stream_put(stream);
+ return fd;
+}
+
__bpf_kfunc_start_defs();
/*
@@ -238,6 +411,11 @@ __bpf_kfunc int bpf_stream_vprintk(int stream_id, const char *fmt__str, const vo
return ret;
ret = bstr_printf(data.buf, MAX_BPRINTF_BUF, fmt__str, data.bin_args);
+ /* Truncation: reject before capacity charge (not -ENOMEM). */
+ if (ret >= MAX_BPRINTF_BUF) {
+ bpf_bprintf_cleanup(&data);
+ return -E2BIG;
+ }
/* Exclude NULL byte during push. */
ret = bpf_stream_push_str(stream, data.buf, ret);
bpf_bprintf_cleanup(&data);
@@ -268,28 +446,47 @@ __bpf_kfunc_end_defs();
/* Added kfunc to common_btf_ids */
-void bpf_prog_stream_init(struct bpf_prog *prog)
+int bpf_prog_stream_init(struct bpf_prog *prog, gfp_t gfp_extra_flags)
{
int i;
for (i = 0; i < ARRAY_SIZE(prog->aux->stream); i++) {
- atomic_set(&prog->aux->stream[i].capacity, 0);
- init_llist_head(&prog->aux->stream[i].log);
- mutex_init(&prog->aux->stream[i].lock);
- prog->aux->stream[i].backlog_head = NULL;
- prog->aux->stream[i].backlog_tail = NULL;
+ struct bpf_stream *stream;
+
+ /* On failure, bpf_prog_stream_free() releases the streams allocated so far. */
+ stream = kzalloc_obj(*stream,
+ bpf_memcg_flags(GFP_KERNEL | gfp_extra_flags));
+ if (!stream)
+ return -ENOMEM;
+
+ refcount_set(&stream->refcnt, 1);
+ init_llist_head(&stream->log);
+ mutex_init(&stream->lock);
+ init_waitqueue_head(&stream->waitq);
+ init_irq_work(&stream->notify_work, bpf_stream_notify);
+ prog->aux->stream[i] = stream;
}
+ return 0;
}
void bpf_prog_stream_free(struct bpf_prog *prog)
{
- struct llist_node *list;
int i;
for (i = 0; i < ARRAY_SIZE(prog->aux->stream); i++) {
- list = llist_del_all(&prog->aux->stream[i].log);
- bpf_stream_free_list(list);
- bpf_stream_free_list(prog->aux->stream[i].backlog_head);
+ struct bpf_stream *stream = prog->aux->stream[i];
+
+ if (!stream)
+ continue;
+ /*
+ * Pairs with smp_load_acquire() in bpf_stream_file_read(): every
+ * publication precedes the dead flag, so a reader that observes
+ * it also observes all buffered data.
+ */
+ smp_store_release(&stream->dead, true);
+ wake_up_interruptible_poll(&stream->waitq, EPOLLHUP);
+ bpf_stream_put(stream);
+ prog->aux->stream[i] = NULL;
}
}
@@ -311,17 +508,18 @@ int bpf_stream_stage_printk(struct bpf_stream_stage *ss, const char *fmt, ...)
{
struct bpf_bprintf_buffers *buf;
va_list args;
- int ret;
+ int len, ret;
if (bpf_try_get_buffers(&buf))
return -EBUSY;
va_start(args, fmt);
- ret = vsnprintf(buf->buf, ARRAY_SIZE(buf->buf), fmt, args);
+ len = vscnprintf(buf->buf, ARRAY_SIZE(buf->buf), fmt, args);
va_end(args);
- ss->len += ret;
- /* Exclude NULL byte during push. */
- ret = __bpf_stream_push_str(&ss->log, buf->buf, ret);
+ /* Exclude NULL byte during push; skip empty output entirely. */
+ ret = len ? __bpf_stream_push_str(&ss->log, buf->buf, len) : 0;
+ if (!ret)
+ ss->len += len;
bpf_put_buffers();
return ret;
}
@@ -351,6 +549,7 @@ int bpf_stream_stage_commit(struct bpf_stream_stage *ss, struct bpf_prog *prog,
list = tail;
}
llist_add_batch(head, tail, &stream->log);
+ bpf_stream_publish(stream, ss->len);
return 0;
}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 96217b99399d..90bc7230afa8 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -602,38 +602,45 @@ static bool can_alloc_pages(void)
!IS_ENABLED(CONFIG_PREEMPT_RT);
}
-static struct page *__bpf_alloc_page(int nid)
+struct page *bpf_alloc_page(int nid, bool sleepable)
{
- if (!can_alloc_pages())
+ if (!sleepable || !can_alloc_pages())
return alloc_pages_nolock(__GFP_ACCOUNT, nid, 0);
return alloc_pages_node(nid,
GFP_KERNEL | __GFP_ZERO | __GFP_ACCOUNT
- | __GFP_NOWARN,
+ | __GFP_NOWARN | __GFP_RETRY_MAYFAIL,
0);
}
-int bpf_map_alloc_pages(const struct bpf_map *map, int nid,
- unsigned long nr_pages, struct page **pages)
+void bpf_free_pages(struct llist_head *pages)
{
- unsigned long i, j;
+ struct llist_node *node;
+ struct page *page, *tmp;
+
+ node = llist_del_all(pages);
+ llist_for_each_entry_safe(page, tmp, node, pcp_llist)
+ free_pages_nolock(page, 0);
+}
+
+int bpf_alloc_pages(int nid, unsigned long nr_pages,
+ struct llist_head *pages, bool sleepable)
+{
+ unsigned long i;
struct page *pg;
- int ret = 0;
for (i = 0; i < nr_pages; i++) {
- pg = __bpf_alloc_page(nid);
-
- if (pg) {
- pages[i] = pg;
- continue;
- }
- for (j = 0; j < i; j++)
- free_pages_nolock(pages[j], 0);
- ret = -ENOMEM;
- break;
+ pg = bpf_alloc_page(nid, sleepable);
+ if (!pg)
+ goto free_pages;
+ llist_add(&pg->pcp_llist, pages);
}
- return ret;
+ return 0;
+
+free_pages:
+ bpf_free_pages(pages);
+ return -ENOMEM;
}
static int btf_field_cmp(const void *a, const void *b)
@@ -687,6 +694,7 @@ void btf_record_free(struct btf_record *rec)
case BPF_REFCOUNT:
case BPF_WORKQUEUE:
case BPF_TASK_WORK:
+ case BPF_RCU_HEAD:
/* Nothing to release */
break;
default:
@@ -741,6 +749,7 @@ struct btf_record *btf_record_dup(const struct btf_record *rec)
case BPF_REFCOUNT:
case BPF_WORKQUEUE:
case BPF_TASK_WORK:
+ case BPF_RCU_HEAD:
/* Nothing to acquire */
break;
default:
@@ -874,6 +883,7 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj)
case BPF_LIST_NODE:
case BPF_RB_NODE:
case BPF_REFCOUNT:
+ case BPF_RCU_HEAD:
break;
default:
WARN_ON_ONCE(1);
@@ -954,6 +964,9 @@ void bpf_map_put(struct bpf_map *map)
/* bpf_map_free_id() must be called first */
bpf_map_free_id(map);
+ if (map->ops->map_free_pre_rcu)
+ map->ops->map_free_pre_rcu(map);
+
WARN_ON_ONCE(atomic64_read(&map->sleepable_refcnt));
/* RCU tasks trace grace period implies RCU grace period. */
if (READ_ONCE(map->free_after_mult_rcu_gp))
@@ -1277,7 +1290,7 @@ static int map_check_btf(struct bpf_map *map, struct bpf_token *token,
map->record = btf_parse_fields(btf, value_type,
BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK | BPF_TIMER | BPF_KPTR | BPF_LIST_HEAD |
BPF_RB_ROOT | BPF_REFCOUNT | BPF_WORKQUEUE | BPF_UPTR |
- BPF_TASK_WORK,
+ BPF_TASK_WORK | BPF_RCU_HEAD,
map->value_size);
if (!IS_ERR_OR_NULL(map->record)) {
int i;
@@ -1319,6 +1332,12 @@ static int map_check_btf(struct bpf_map *map, struct bpf_token *token,
goto free_map_tab;
}
break;
+ case BPF_RCU_HEAD:
+ if (map->map_type != BPF_MAP_TYPE_ARRAY) {
+ ret = -EOPNOTSUPP;
+ goto free_map_tab;
+ }
+ break;
case BPF_KPTR_UNREF:
case BPF_KPTR_REF:
case BPF_KPTR_PERCPU:
@@ -2441,6 +2460,9 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
{
struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
+ btf_put(aux->btf);
+ kvfree(aux->jited_linfo);
+ kvfree(aux->linfo);
kvfree(aux->func_info);
kfree(aux->func_info_aux);
free_uid(aux->user);
@@ -2457,7 +2479,7 @@ static void __bpf_prog_put_rcu_tasks(struct rcu_head *rcu)
{
struct bpf_prog *prog = container_of(rcu, struct bpf_prog_aux, rcu)->prog;
- if (prog->sleepable)
+ if (bpf_prog_runs_sleepable(prog))
call_rcu_tasks_trace(rcu, __bpf_prog_put_rcu);
else
call_rcu(rcu, __bpf_prog_put_rcu);
@@ -2465,11 +2487,7 @@ static void __bpf_prog_put_rcu_tasks(struct rcu_head *rcu)
static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)
{
- bpf_prog_kallsyms_del_all(prog);
- btf_put(prog->aux->btf);
module_put(prog->aux->mod);
- kvfree(prog->aux->jited_linfo);
- kvfree(prog->aux->linfo);
kfree(prog->aux->kfunc_tab);
kfree(prog->aux->ctx_arg_info);
if (prog->aux->attach_btf)
@@ -2478,7 +2496,7 @@ static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)
if (deferred) {
if (IS_ENABLED(CONFIG_TASKS_RCU) && prog->aux->tramp_linked)
call_rcu_tasks(&prog->aux->rcu, __bpf_prog_put_rcu_tasks);
- else if (prog->sleepable)
+ else if (bpf_prog_runs_sleepable(prog))
call_rcu_tasks_trace(&prog->aux->rcu, __bpf_prog_put_rcu);
else
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
@@ -2953,9 +2971,13 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
BPF_F_XDP_HAS_FRAGS |
BPF_F_XDP_DEV_BOUND_ONLY |
BPF_F_TEST_REG_INVARIANTS |
+ BPF_F_ARENA_SCALAR |
BPF_F_TOKEN_FD))
return -EINVAL;
+ if ((attr->prog_flags & BPF_F_ARENA_SCALAR) && !bpf_jit_supports_arena_scalar())
+ return -EOPNOTSUPP;
+
bpf_prog_load_fixup_attach_type(attr);
if (attr->prog_flags & BPF_F_TOKEN_FD) {
@@ -3085,6 +3107,10 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
prog->aux->user = get_current_user();
prog->len = attr->insn_cnt;
+ err = bpf_prog_stream_init(prog, GFP_USER);
+ if (err)
+ goto free_prog;
+
err = -EFAULT;
if (copy_from_bpfptr(prog->insns,
make_bpfptr(attr->insns, uattr.is_kernel),
@@ -3824,6 +3850,19 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
err = -EINVAL;
goto out_unlock;
}
+ /*
+ * The size of these contexts depends on what the program is attached
+ * to, or run with. That is compared with max_ctx_offset of the program,
+ * and nobody looks at the one of its extensions.
+ */
+ if (prog->type == BPF_PROG_TYPE_EXT &&
+ (tgt_prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT ||
+ tgt_prog->type == BPF_PROG_TYPE_TRACEPOINT ||
+ tgt_prog->type == BPF_PROG_TYPE_SYSCALL) &&
+ prog->aux->max_ctx_offset > tgt_prog->aux->max_ctx_offset) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
err = bpf_link_prime(&link->link.link, &link_primer);
if (err)
@@ -3993,19 +4032,19 @@ static int bpf_perf_link_fill_common(const struct perf_event *event,
*ulenp = 1;
}
if (!uname)
- return 0;
+ goto out;
if (buf) {
err = bpf_copy_to_user(uname, buf, ulen, len);
- if (err)
- return err;
} else {
char zero = '\0';
if (put_user(zero, uname))
- return -EFAULT;
+ err = -EFAULT;
}
- return 0;
+out:
+ kfree_const(buf);
+ return err;
}
#ifdef CONFIG_KPROBE_EVENTS
@@ -4062,6 +4101,7 @@ static void bpf_perf_link_fdinfo_kprobe(const struct perf_event *event,
name, offset, missed, addr,
type == BPF_FD_TYPE_KRETPROBE ? "kretprobe" : "kprobe",
event->bpf_cookie);
+ kfree_const(name);
}
#endif
@@ -4115,6 +4155,7 @@ static void bpf_perf_link_fdinfo_uprobe(const struct perf_event *event,
name, offset, ref_ctr_offset,
type == BPF_FD_TYPE_URETPROBE ? "uretprobe" : "uprobe",
event->bpf_cookie);
+ kfree_const(name);
}
#endif
@@ -4213,6 +4254,7 @@ static void bpf_tracepoint_link_show_fdinfo(const struct perf_event *event,
"event_type:\t%s\n"
"cookie:\t%llu\n",
name, "tracepoint", event->bpf_cookie);
+ kfree_const(name);
}
static void bpf_probe_link_show_fdinfo(const struct perf_event *event,
@@ -4772,6 +4814,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
case BPF_CGROUP_GETSOCKOPT:
case BPF_CGROUP_SETSOCKOPT:
case BPF_LSM_CGROUP:
+ case BPF_STRUCT_OPS:
return cgroup_bpf_prog_query(attr, uattr, uattr_size);
case BPF_LIRC_MODE2:
return lirc_prog_query(attr, uattr);
@@ -5044,6 +5087,12 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
insns[i].imm = BPF_FUNC_tail_call;
/* fall-through */
}
+ /*
+ * BPF_JCOND is the same opcode as BPF_CALL_ARGS. The latter is for
+ * the interpreter, and a prog with unwind_goto is always JITed.
+ */
+ if (prog->jited && bpf_is_unwind_goto(&insns[i]))
+ continue;
if (code == (BPF_JMP | BPF_CALL) ||
code == (BPF_JMP | BPF_CALL_ARGS)) {
/* Restore the legacy xlated dump layout. */
@@ -5420,7 +5469,11 @@ static int bpf_map_get_info_by_fd(struct file *file,
info_len = min_t(u32, sizeof(info), info_len);
memset(&info, 0, sizeof(info));
- if (copy_from_user(&info, uinfo, info_len))
+ if (info_len >= offsetofend(struct bpf_map_info, hash) &&
+ get_user(info.hash, &uinfo->hash))
+ return -EFAULT;
+ if (info_len >= offsetofend(struct bpf_map_info, hash_size) &&
+ get_user(info.hash_size, &uinfo->hash_size))
return -EFAULT;
info.type = map->map_type;
@@ -5735,11 +5788,13 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
err = bpf_get_perf_event_info(event, &prog_id, &fd_type,
&buf, &probe_offset,
&probe_addr, NULL);
- if (!err)
+ if (!err) {
err = bpf_task_fd_query_copy(attr, uattr, prog_id,
fd_type, buf,
probe_offset,
probe_addr);
+ kfree_const(buf);
+ }
goto put_file;
}
@@ -6220,7 +6275,7 @@ static int bpf_prog_bind_map(union bpf_attr *attr)
/* The bpf program will not access the bpf map, but for the sake of
* simplicity, increase sleepable_refcnt for sleepable program as well.
*/
- if (prog->sleepable)
+ if (bpf_prog_runs_sleepable(prog))
atomic64_inc(&map->sleepable_refcnt);
memcpy(used_maps_new, used_maps_old,
sizeof(used_maps_old[0]) * prog->aux->used_map_cnt);
@@ -6320,6 +6375,28 @@ put_prog:
return ret;
}
+#define BPF_PROG_STREAM_OPEN_LAST_FIELD prog_stream_open.flags
+
+static int prog_stream_open(union bpf_attr *attr)
+{
+ struct bpf_prog *prog;
+ u32 flags = attr->prog_stream_open.flags;
+ int ret;
+
+ if (CHECK_ATTR(BPF_PROG_STREAM_OPEN))
+ return -EINVAL;
+ if (flags & ~BPF_F_STREAM_NONBLOCK)
+ return -EINVAL;
+
+ prog = bpf_prog_get(attr->prog_stream_open.prog_fd);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ ret = bpf_prog_stream_new_fd(prog, attr->prog_stream_open.stream_id, flags);
+ bpf_prog_put(prog);
+ return ret;
+}
+
static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
bpfptr_t uattr_common, unsigned int size_common)
{
@@ -6492,6 +6569,9 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
case BPF_PROG_ASSOC_STRUCT_OPS:
err = prog_assoc_struct_ops(&attr);
break;
+ case BPF_PROG_STREAM_OPEN:
+ err = prog_stream_open(&attr);
+ break;
default:
err = -EINVAL;
break;
diff --git a/kernel/bpf/sysfs_btf.c b/kernel/bpf/sysfs_btf.c
index 9cbe15ce3540..556fe92c3649 100644
--- a/kernel/bpf/sysfs_btf.c
+++ b/kernel/bpf/sysfs_btf.c
@@ -6,8 +6,10 @@
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/mm.h>
+#include <linux/vmalloc.h>
#include <linux/io.h>
#include <linux/btf.h>
@@ -15,16 +17,12 @@
extern char __start_BTF[];
extern char __stop_BTF[];
-static int btf_sysfs_vmlinux_mmap(struct file *filp, struct kobject *kobj,
- const struct bin_attribute *attr,
- struct vm_area_struct *vma)
+static int btf_sysfs_mmap_check(void *data, size_t size, struct vm_area_struct *vma)
{
- unsigned long pages = PAGE_ALIGN(attr->size) >> PAGE_SHIFT;
+ unsigned long pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
size_t vm_size = vma->vm_end - vma->vm_start;
- phys_addr_t addr = __pa_symbol(__start_BTF);
- unsigned long pfn = addr >> PAGE_SHIFT;
- if (attr->private != __start_BTF || !PAGE_ALIGNED(addr))
+ if (!data || !PAGE_ALIGNED((unsigned long)data))
return -EINVAL;
if (vma->vm_pgoff)
@@ -33,24 +31,153 @@ static int btf_sysfs_vmlinux_mmap(struct file *filp, struct kobject *kobj,
if (vma->vm_flags & (VM_WRITE | VM_EXEC | VM_MAYSHARE))
return -EACCES;
- if (pfn + pages < pfn)
- return -EINVAL;
-
if ((vm_size >> PAGE_SHIFT) > pages)
return -EINVAL;
vm_flags_mod(vma, VM_DONTDUMP, VM_MAYEXEC | VM_MAYWRITE);
+ return 0;
+}
+
+static int btf_sysfs_mmap_image(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ void *data = READ_ONCE(attr->private);
+ phys_addr_t addr;
+ unsigned long pfn;
+ size_t vm_size = vma->vm_end - vma->vm_start;
+ int err;
+
+ err = btf_sysfs_mmap_check(data, attr->size, vma);
+ if (err)
+ return err;
+
+ /* Kernel image addresses can lie inside the vmalloc range on arm64. */
+ addr = __pa_symbol(data);
+ pfn = addr >> PAGE_SHIFT;
+ if (pfn + (PAGE_ALIGN(attr->size) >> PAGE_SHIFT) < pfn)
+ return -EINVAL;
+
return remap_pfn_range(vma, vma->vm_start, pfn, vm_size, vma->vm_page_prot);
}
static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = {
.attr = { .name = "vmlinux", .mode = 0444, },
.read = sysfs_bin_attr_simple_read,
- .mmap = btf_sysfs_vmlinux_mmap,
+ .mmap = btf_sysfs_mmap_image,
};
struct kobject *btf_kobj;
+struct btf_sysfs_entry {
+ struct bin_attribute attr;
+ char *module_name;
+};
+
+static void *btf_sysfs_lazy_data(const struct bin_attribute *attr)
+{
+ struct btf_sysfs_entry *entry = container_of(attr, struct btf_sysfs_entry, attr);
+ void *data;
+
+ data = smp_load_acquire(&attr->private);
+ if (!data) {
+ request_module("%s", entry->module_name);
+ data = smp_load_acquire(&attr->private);
+ }
+ return data;
+}
+
+static ssize_t btf_sysfs_read_lazy(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ void *data = btf_sysfs_lazy_data(attr);
+
+ if (!data)
+ return -ENODEV;
+
+ return memory_read_from_buffer(buf, count, &off, data, attr->size);
+}
+
+static int btf_sysfs_mmap_lazy(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ void *data = btf_sysfs_lazy_data(attr);
+ int err;
+
+ if (!data)
+ return -ENODEV;
+
+ err = btf_sysfs_mmap_check(data, attr->size, vma);
+ if (err)
+ return err;
+
+ return remap_vmalloc_range(vma, data, 0);
+}
+
+struct bin_attribute *sysfs_btf_add(const char *name, void *data, size_t data_size,
+ bool mmap, const char *lazy_module_name)
+{
+ struct btf_sysfs_entry *entry;
+ struct bin_attribute *attr;
+ int err;
+
+ entry = kzalloc_obj(*entry);
+ if (!entry)
+ return ERR_PTR(-ENOMEM);
+
+ attr = &entry->attr;
+ sysfs_bin_attr_init(attr);
+ attr->attr.mode = 0444;
+ attr->size = data_size;
+ attr->private = data;
+ attr->read = lazy_module_name ? btf_sysfs_read_lazy : sysfs_bin_attr_simple_read;
+ if (mmap)
+ attr->mmap = lazy_module_name ? btf_sysfs_mmap_lazy : btf_sysfs_mmap_image;
+ attr->attr.name = kstrdup(name, GFP_KERNEL);
+ if (!attr->attr.name) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+ if (lazy_module_name) {
+ entry->module_name = kstrdup(lazy_module_name, GFP_KERNEL);
+ if (!entry->module_name) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+ }
+
+ err = sysfs_create_bin_file(btf_kobj, attr);
+ if (err) {
+ pr_warn("failed to register [%s] BTF in sysfs: %d\n", name, err);
+ goto err_free;
+ }
+
+ return attr;
+
+err_free:
+ kfree(entry->module_name);
+ kfree(attr->attr.name);
+ kfree(entry);
+ return ERR_PTR(err);
+}
+
+bool sysfs_btf_update(struct bin_attribute *attr, void *data)
+{
+ return attr && !cmpxchg(&attr->private, NULL, data);
+}
+
+void sysfs_btf_remove(struct bin_attribute *attr)
+{
+ struct btf_sysfs_entry *entry = container_of(attr, struct btf_sysfs_entry, attr);
+
+ sysfs_remove_bin_file(btf_kobj, attr);
+ kfree(entry->module_name);
+ kfree(attr->attr.name);
+ kfree(entry);
+}
+
static int __init btf_vmlinux_init(void)
{
bin_attr_btf_vmlinux.private = __start_BTF;
diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
index c65ba1dcd866..2ee7b3b3c10b 100644
--- a/kernel/bpf/task_iter.c
+++ b/kernel/bpf/task_iter.c
@@ -737,7 +737,11 @@ static const struct bpf_iter_seq_info task_vma_seq_info = {
static struct bpf_iter_reg task_vma_reg_info = {
.target = "task_vma",
.attach_target = bpf_iter_attach_task,
- .feature = BPF_ITER_RESCHED,
+ /*
+ * ->show() runs with the mmap_lock of the iterated mm held, which a
+ * sleepable program takes again when it faults on user memory.
+ */
+ .feature = BPF_ITER_RESCHED | BPF_ITER_NOSLEEP,
.ctx_arg_info_size = 2,
.ctx_arg_info = {
{ offsetof(struct bpf_iter__task_vma, task),
@@ -806,8 +810,14 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
vma = find_vma(mm, start);
if (vma && vma->vm_start <= start && vma->vm_end > start) {
+ /*
+ * The callback must not sleep while mmap_lock is held, and the
+ * verifier checks it as an RCU read-side critical section.
+ */
+ rcu_read_lock();
callback_fn((u64)(long)task, (u64)(long)vma,
(u64)(long)callback_ctx, 0, 0);
+ rcu_read_unlock();
ret = 0;
}
bpf_mmap_unlock_mm(work, mm);
diff --git a/kernel/bpf/tnum.c b/kernel/bpf/tnum.c
index ec9c310cf5d7..1f620facd1b7 100644
--- a/kernel/bpf/tnum.c
+++ b/kernel/bpf/tnum.c
@@ -317,3 +317,14 @@ u64 tnum_step(struct tnum t, u64 z)
inc = (filled + 1) & t.mask;
return t.value | inc;
}
+
+/*
+ * Return the number of trailing bits known to be zero in a tnum.
+ * Return 64 for a tnum representing zero.
+ */
+u32 tnum_alignment(struct tnum a)
+{
+ u64 v = a.value | a.mask;
+
+ return v ? __ffs64(v) : 64;
+}
diff --git a/kernel/bpf/token.c b/kernel/bpf/token.c
index da915a4f972b..aa42243af16b 100644
--- a/kernel/bpf/token.c
+++ b/kernel/bpf/token.c
@@ -169,8 +169,11 @@ int bpf_token_create(union bpf_attr *attr)
FD_PREPARE(fdf, O_CLOEXEC,
alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME,
O_RDWR, &bpf_token_fops));
- if (fdf->fd < 0)
+ if (fdf->fd < 0) {
+ /* no file took over the inode reference */
+ iput(inode);
return fdf->fd;
+ }
token = kzalloc_obj(*token, GFP_USER);
if (!token)
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index bf4cb3dd444d..21d460866dfe 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -475,18 +475,11 @@ static int register_fentry(struct bpf_trampoline *tr, struct bpf_tramp_image *im
void *data __maybe_unused)
{
void *new_addr = im->image;
- void *ip = tr->func.addr;
- unsigned long faddr;
int ret;
- faddr = ftrace_location((unsigned long)ip);
- if (faddr) {
+ if (tr->func.ftrace_managed) {
if (!tr->fops)
return -ENOTSUPP;
- tr->func.ftrace_managed = true;
- }
-
- if (tr->func.ftrace_managed) {
ret = direct_ops_add(tr, new_addr);
} else {
ret = bpf_trampoline_update_fentry(tr, 0, NULL, new_addr);
@@ -859,7 +852,7 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
}
}
-static int bpf_freplace_check_tgt_prog(struct bpf_prog *tgt_prog)
+static int bpf_freplace_link_tgt_prog(struct bpf_prog *tgt_prog)
{
struct bpf_prog_aux *aux = tgt_prog->aux;
@@ -873,7 +866,7 @@ static int bpf_freplace_check_tgt_prog(struct bpf_prog *tgt_prog)
*/
return -EBUSY;
- aux->is_extended = true;
+ aux->freplace_link_cnt++;
return 0;
}
@@ -1023,13 +1016,20 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_node *node,
/* Cannot attach extension if fentry/fexit are in use. */
if (cnt)
return -EBUSY;
- err = bpf_freplace_check_tgt_prog(tgt_prog);
+ err = bpf_freplace_link_tgt_prog(tgt_prog);
if (err)
return err;
+ err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_NOP,
+ BPF_MOD_JUMP, NULL,
+ node->link->prog->bpf_func);
+ if (err) {
+ /* Undo the claim from bpf_freplace_link_tgt_prog(). */
+ guard(mutex)(&tgt_prog->aux->ext_mutex);
+ tgt_prog->aux->freplace_link_cnt--;
+ return err;
+ }
tr->extension_prog = node->link->prog;
- return bpf_arch_text_poke(tr->func.addr, BPF_MOD_NOP,
- BPF_MOD_JUMP, NULL,
- node->link->prog->bpf_func);
+ return 0;
}
err = bpf_trampoline_add_prog(tr, node, cnt);
if (err)
@@ -1069,7 +1069,7 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_node *node,
tr->extension_prog->bpf_func, NULL);
tr->extension_prog = NULL;
guard(mutex)(&tgt_prog->aux->ext_mutex);
- tgt_prog->aux->is_extended = false;
+ tgt_prog->aux->freplace_link_cnt--;
return err;
}
bpf_trampoline_remove_prog(tr, node);
@@ -1279,6 +1279,7 @@ struct bpf_trampoline *bpf_trampoline_get(u64 key,
memcpy(&tr->func.model, &tgt_info->fmodel, sizeof(tgt_info->fmodel));
tr->func.addr = (void *)tgt_info->tgt_addr;
+ tr->func.ftrace_managed = tr->ip != 0;
out:
trampoline_unlock(tr);
return tr;
@@ -1628,13 +1629,20 @@ static void ftrace_hash_add(struct ftrace_hash *hash, struct ftrace_func_entry *
static int register_fentry_multi(struct bpf_trampoline *tr, struct bpf_tramp_image *im, void *ptr)
{
unsigned long addr = (unsigned long) im->image;
- unsigned long ip = ftrace_location(tr->ip);
struct bpf_tracing_multi_data *data = ptr;
+ unsigned long ip = tr->ip;
+
+ /*
+ * Different BTF ids get different trampolines, but they can still
+ * resolve to the same address (aliases, functions of the same name).
+ * There's only one call site to attach to.
+ */
+ if (ftrace_lookup_ip(data->reg, ip))
+ return -EINVAL;
if (bpf_trampoline_use_jmp(tr->flags))
addr = ftrace_jmp_set(addr);
- tr->func.ftrace_managed = true;
ftrace_hash_add(data->reg, data->entry, ip, addr);
tr->cur_image = im;
return 0;
@@ -1643,13 +1651,12 @@ static int register_fentry_multi(struct bpf_trampoline *tr, struct bpf_tramp_ima
static int unregister_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags, void *ptr)
{
unsigned long addr = (unsigned long) tr->cur_image->image;
- unsigned long ip = ftrace_location(tr->ip);
struct bpf_tracing_multi_data *data = ptr;
if (bpf_trampoline_use_jmp(tr->flags))
addr = ftrace_jmp_set(addr);
- ftrace_hash_add(data->unreg, data->entry, ip, addr);
+ ftrace_hash_add(data->unreg, data->entry, tr->ip, addr);
tr->cur_image = NULL;
return 0;
}
@@ -1658,13 +1665,12 @@ static int modify_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags, struct
bool lock_direct_mutex, void *ptr)
{
unsigned long addr = (unsigned long) im->image;
- unsigned long ip = ftrace_location(tr->ip);
struct bpf_tracing_multi_data *data = ptr;
if (bpf_trampoline_use_jmp(tr->flags))
addr = ftrace_jmp_set(addr);
- ftrace_hash_add(data->modify, data->entry, ip, addr);
+ ftrace_hash_add(data->modify, data->entry, tr->ip, addr);
tr->cur_image = im;
return 0;
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5f874979b8d7..5601d2056218 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5,6 +5,7 @@
*/
#include <uapi/linux/btf.h>
#include <linux/bpf-cgroup.h>
+#include <linux/count_zeros.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
@@ -24,11 +25,13 @@
#include <linux/bpf_lsm.h>
#include <linux/security.h>
#include <linux/verification.h>
+#include <linux/keyctl.h>
#include <linux/btf_ids.h>
#include <linux/poison.h>
#include <linux/module.h>
#include <linux/cpumask.h>
#include <linux/cnum.h>
+#include <linux/gcd.h>
#include <linux/bpf_mem_alloc.h>
#include <net/xdp.h>
#include <linux/trace_events.h>
@@ -216,6 +219,7 @@ static int ref_set_non_owning(struct bpf_verifier_env *env,
struct bpf_reg_state *reg);
static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg);
static inline bool in_sleepable_context(struct bpf_verifier_env *env);
+static bool in_rcu_callback(struct bpf_verifier_env *env);
static const char *non_sleepable_context_description(struct bpf_verifier_env *env);
static void scalar32_min_max_add(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg);
static void scalar_min_max_add(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg);
@@ -301,6 +305,12 @@ static int arg_idx_from_argno(argno_t a)
return arg_from_argno(a) - 1;
}
+/* Normalize helper register numbers and kfunc argument numbers to ABI slots. */
+static u32 arg_slot_from_argno(argno_t a)
+{
+ return abs(a.argno) - 1;
+}
+
static const char *btf_type_name(const struct btf *btf, u32 id)
{
return btf_name_by_offset(btf, btf_type_by_id(btf, id)->name_off);
@@ -394,27 +404,69 @@ bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog)
return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL;
}
-static bool subprog_returns_void(struct bpf_verifier_env *env, int subprog)
+static const struct btf_type *subprog_ret_type(struct bpf_verifier_env *env, int subprog)
{
- const struct btf_type *type, *func, *func_proto;
+ const struct btf_type *func, *func_proto;
const struct btf *btf = env->prog->aux->btf;
u32 btf_id;
+ if (!btf || !env->prog->aux->func_info)
+ return NULL;
+
btf_id = env->prog->aux->func_info[subprog].type_id;
+ /* Both already validated by prepare_btf_func() at prog load. */
func = btf_type_by_id(btf, btf_id);
- if (verifier_bug_if(!func, env, "btf_id %u not found", btf_id))
- return false;
-
func_proto = btf_type_by_id(btf, func->type);
- if (!func_proto)
- return false;
- type = btf_type_skip_modifiers(btf, func_proto->type, NULL);
- if (!type)
- return false;
+ return btf_type_skip_modifiers(btf, func_proto->type, NULL);
+}
+
+static bool subprog_returns_void(struct bpf_verifier_env *env, int subprog)
+{
+ const struct btf_type *type = subprog_ret_type(env, subprog);
- return btf_type_is_void(type);
+ return type && btf_type_is_void(type);
+}
+
+static u32 ret_regs_cnt(u32 size)
+{
+ return size > 8 && size <= 16 ? 2 : 1;
+}
+
+/* Registers holding a function return value, in order. See ret_regs_cnt(). */
+static const int ret_regs[] = { BPF_REG_0, BPF_REG_2 };
+
+static int bpf_compute_subprog_ret_regs(struct bpf_verifier_env *env)
+{
+ const struct btf *btf = env->prog->aux->btf;
+ const struct btf_type *type;
+ int subprog;
+ u32 size;
+
+ if (!env->prog->jit_requested || bpf_prog_is_offloaded(env->prog->aux))
+ return 0;
+
+ /*
+ * Skip the main program: its return value is the program's exit code,
+ * read out of R0, so it never uses the register pair. An extension does
+ * have a real prototype for subprog 0, but btf_check_func_type_match()
+ * refuses to replace a function returning more than 8 bytes.
+ */
+ for (subprog = 1; subprog < env->subprog_cnt; subprog++) {
+ type = subprog_ret_type(env, subprog);
+ if (!type || btf_type_is_void(type))
+ continue;
+ if (verifier_bug_if(IS_ERR(btf_resolve_size(btf, type, &size)), env,
+ "cannot size return type of subprog %d", subprog))
+ return -EFAULT;
+ if (ret_regs_cnt(size) > 1) {
+ subprog_info(env, subprog)->ret_reg_pair = true;
+ env->prog->jit_required = 1;
+ }
+ }
+
+ return 0;
}
const char *bpf_subprog_name(const struct bpf_verifier_env *env, int subprog)
@@ -486,6 +538,7 @@ static bool is_ptr_cast_function(enum bpf_func_id func_id)
static bool is_sync_callback_calling_kfunc(u32 btf_id);
static bool is_async_callback_calling_kfunc(u32 btf_id);
+static bool is_call_rcu_kfunc(u32 btf_id);
static bool is_callback_calling_kfunc(u32 btf_id);
static bool is_bpf_wq_set_callback_kfunc(u32 btf_id);
@@ -528,6 +581,10 @@ static bool is_async_cb_sleepable(struct bpf_verifier_env *env, struct bpf_insn
if (bpf_helper_call(insn) && insn->imm == BPF_FUNC_timer_set_callback)
return false;
+ /* bpf_call_rcu and bpf_call_rcu_tasks_trace callbacks are never sleepable. */
+ if (bpf_pseudo_kfunc_call(insn) && insn->off == 0 && is_call_rcu_kfunc(insn->imm))
+ return false;
+
/* bpf_wq and bpf_task_work callbacks are always sleepable. */
if (bpf_pseudo_kfunc_call(insn) && insn->off == 0 &&
(is_bpf_wq_set_callback_kfunc(insn->imm) || is_task_work_add_kfunc(insn->imm)))
@@ -537,6 +594,19 @@ static bool is_async_cb_sleepable(struct bpf_verifier_env *env, struct bpf_insn
return false;
}
+/* From here on the maps of the program count it as a sleepable user. */
+static void mark_sleepable_async_cb(struct bpf_verifier_env *env)
+{
+ int i;
+
+ if (bpf_prog_runs_sleepable(env->prog))
+ return;
+
+ env->prog->aux->sleepable_async_cb = true;
+ for (i = 0; i < env->used_map_cnt; i++)
+ atomic64_inc(&env->used_maps[i]->sleepable_refcnt);
+}
+
bool bpf_is_may_goto_insn(struct bpf_insn *insn)
{
return insn->code == (BPF_JMP | BPF_JCOND) && insn->src_reg == BPF_MAY_GOTO;
@@ -544,16 +614,17 @@ bool bpf_is_may_goto_insn(struct bpf_insn *insn)
static bool is_spi_bounds_valid(struct bpf_func_state *state, int spi, int nr_slots)
{
- int allocated_slots = state->allocated_stack / BPF_REG_SIZE;
+ int allocated_slots = bpf_stack_nr_slots(state);
- /* We need to check that slots between [spi - nr_slots + 1, spi] are
- * within [0, allocated_stack).
- *
- * Please note that the spi grows downwards. For example, a dynptr
- * takes the size of two stack slots; the first slot will be at
- * spi and the second slot will be at spi - 1.
- */
- return spi - nr_slots + 1 >= 0 && spi < allocated_slots;
+ /*
+ * We need to check that slots between [spi - nr_slots + 1, spi] are
+ * within [0, allocated_stack).
+ *
+ * Please note that the spi grows downwards. For example, a dynptr
+ * takes the size of two stack slots; the first slot will be at
+ * spi and the second slot will be at spi - 1.
+ */
+ return spi - nr_slots + 1 >= 0 && spi < allocated_slots;
}
static int stack_slot_obj_get_spi(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
@@ -668,6 +739,21 @@ static void mark_dynptr_cb_reg(struct bpf_verifier_env *env,
static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
struct bpf_func_state *state, int spi);
+static int destroy_dynptrs_in_stack_slots(struct bpf_verifier_env *env,
+ struct bpf_func_state *state,
+ int first_spi, int last_spi)
+{
+ int spi, err;
+
+ for (spi = first_spi; spi <= last_spi; spi++) {
+ err = destroy_if_dynptr_stack_slot(env, state, spi);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
enum bpf_arg_type arg_type, int insn_idx,
struct ref_obj_desc *ref_obj, struct bpf_dynptr_desc *dynptr)
@@ -681,7 +767,7 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_
return spi;
/* We cannot assume both spi and spi - 1 belong to the same dynptr,
- * hence we need to call destroy_if_dynptr_stack_slot twice for both,
+ * hence we need to destroy dynptrs in both slots,
* to ensure that for the following example:
* [d1][d1][d2][d2]
* spi 3 2 1 0
@@ -689,16 +775,13 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_
* case they do belong to same dynptr, second call won't see slot_type
* as STACK_DYNPTR and will simply skip destruction.
*/
- err = destroy_if_dynptr_stack_slot(env, state, spi);
- if (err)
- return err;
- err = destroy_if_dynptr_stack_slot(env, state, spi - 1);
+ err = destroy_dynptrs_in_stack_slots(env, state, spi - 1, spi);
if (err)
return err;
for (i = 0; i < BPF_REG_SIZE; i++) {
- state->stack[spi].slot_type[i] = STACK_DYNPTR;
- state->stack[spi - 1].slot_type[i] = STACK_DYNPTR;
+ bpf_stack_slot(state, spi)->slot_type[i] = STACK_DYNPTR;
+ bpf_stack_slot(state, spi - 1)->slot_type[i] = STACK_DYNPTR;
}
type = arg_to_dynptr_type(arg_type);
@@ -731,8 +814,8 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_
parent_id = dynptr->parent_id;
}
- mark_dynptr_stack_regs(env, &state->stack[spi].spilled_ptr,
- &state->stack[spi - 1].spilled_ptr, type, parent_id);
+ mark_dynptr_stack_regs(env, &bpf_stack_slot(state, spi)->spilled_ptr,
+ &bpf_stack_slot(state, spi - 1)->spilled_ptr, type, parent_id);
return 0;
}
@@ -764,7 +847,7 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re
* all clones and derived slices. For non-referenced dynptr, only
* the dynptr and slices derived from it will be invalidated.
*/
- reg = &state->stack[spi].spilled_ptr;
+ reg = &bpf_stack_slot(state, spi)->spilled_ptr;
return release_reference(env, dynptr_type_referenced(reg->dynptr.type)
? reg->parent_id
: reg->id);
@@ -803,6 +886,7 @@ static int dynptr_ref_cnt(struct bpf_verifier_env *env, int v_parent_id)
static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
struct bpf_func_state *state, int spi)
{
+ struct bpf_stack_state *slot = bpf_stack_slot(state, spi);
int err = 0;
/* We always ensure that STACK_DYNPTR is never set partially,
@@ -810,20 +894,22 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
* different for STACK_SPILL, where it may be only set for
* 1 byte, so code has to use is_spilled_reg.
*/
- if (state->stack[spi].slot_type[0] != STACK_DYNPTR)
+ if (slot->slot_type[0] != STACK_DYNPTR)
return 0;
/* Reposition spi to first slot */
- if (!state->stack[spi].spilled_ptr.dynptr.first_slot)
+ if (!slot->spilled_ptr.dynptr.first_slot) {
spi = spi + 1;
+ slot = bpf_stack_slot(state, spi);
+ }
/*
* A referenced dynptr can be overwritten only if there is at
* least one other dynptr sharing the same virtual ref parent,
* ensuring the reference can still be properly released.
*/
- if (dynptr_type_referenced(state->stack[spi].spilled_ptr.dynptr.type) &&
- dynptr_ref_cnt(env, state->stack[spi].spilled_ptr.parent_id) <= 1) {
+ if (dynptr_type_referenced(slot->spilled_ptr.dynptr.type) &&
+ dynptr_ref_cnt(env, slot->spilled_ptr.parent_id) <= 1) {
verbose(env, "cannot overwrite referenced dynptr\n");
bpf_diag_res(
env, env->insn_idx, "referenced dynptr overwrite",
@@ -833,7 +919,7 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
}
/* Invalidate the dynptr and any derived slices */
- err = release_reference(env, state->stack[spi].spilled_ptr.id);
+ err = release_reference(env, slot->spilled_ptr.id);
if (!err) {
mark_stack_slot_scratched(env, spi);
mark_stack_slot_scratched(env, spi - 1);
@@ -873,6 +959,7 @@ static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_
static bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
{
struct bpf_func_state *state = bpf_func(env, reg);
+ struct bpf_stack_state *slot;
int i, spi;
/* This already represents first slot of initialized bpf_dynptr.
@@ -887,12 +974,13 @@ static bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, struct bpf_re
spi = dynptr_get_spi(env, reg);
if (spi < 0)
return false;
- if (!state->stack[spi].spilled_ptr.dynptr.first_slot)
+ slot = bpf_stack_slot(state, spi);
+ if (!slot->spilled_ptr.dynptr.first_slot)
return false;
for (i = 0; i < BPF_REG_SIZE; i++) {
- if (state->stack[spi].slot_type[i] != STACK_DYNPTR ||
- state->stack[spi - 1].slot_type[i] != STACK_DYNPTR)
+ if (slot->slot_type[i] != STACK_DYNPTR ||
+ bpf_stack_slot(state, spi - 1)->slot_type[i] != STACK_DYNPTR)
return false;
}
@@ -911,14 +999,23 @@ static enum bpf_dynptr_type dynptr_reg_type(struct bpf_verifier_env *env, struct
if (spi < 0)
return BPF_DYNPTR_TYPE_INVALID;
state = bpf_func(env, reg);
- return state->stack[spi].spilled_ptr.dynptr.type;
+ return bpf_stack_slot(state, spi)->spilled_ptr.dynptr.type;
+}
+
+static bool dynptr_write_changes_pkt_data(enum bpf_dynptr_type type)
+{
+ return type == BPF_DYNPTR_TYPE_SKB || type == BPF_DYNPTR_TYPE_SKB_META;
}
static bool is_dynptr_type_expected(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
enum bpf_arg_type arg_type)
{
- /* ARG_PTR_TO_DYNPTR takes any type of dynptr */
- if (arg_type == ARG_PTR_TO_DYNPTR)
+ /*
+ * ARG_PTR_TO_DYNPTR without a type flag takes any type of dynptr.
+ * Test the flags rather than the whole arg_type, which may carry
+ * unrelated ones such as PTR_MAYBE_NULL.
+ */
+ if (!(arg_type & DYNPTR_TYPE_FLAG_MASK))
return true;
return dynptr_reg_type(env, reg) == arg_to_dynptr_type(arg_type);
@@ -947,7 +1044,7 @@ static int mark_stack_slots_iter(struct bpf_verifier_env *env,
return id;
for (i = 0; i < nr_slots; i++) {
- struct bpf_stack_state *slot = &state->stack[spi - i];
+ struct bpf_stack_state *slot = bpf_stack_slot(state, spi - i);
struct bpf_reg_state *st = &slot->spilled_ptr;
__mark_reg_known_zero(st);
@@ -984,7 +1081,7 @@ static int unmark_stack_slots_iter(struct bpf_verifier_env *env,
return spi;
for (i = 0; i < nr_slots; i++) {
- struct bpf_stack_state *slot = &state->stack[spi - i];
+ struct bpf_stack_state *slot = bpf_stack_slot(state, spi - i);
struct bpf_reg_state *st = &slot->spilled_ptr;
if (i == 0)
@@ -1018,7 +1115,7 @@ static bool is_iter_reg_valid_uninit(struct bpf_verifier_env *env,
return false;
for (i = 0; i < nr_slots; i++) {
- struct bpf_stack_state *slot = &state->stack[spi - i];
+ struct bpf_stack_state *slot = bpf_stack_slot(state, spi - i);
for (j = 0; j < BPF_REG_SIZE; j++)
if (slot->slot_type[j] == STACK_ITER)
@@ -1039,7 +1136,7 @@ static int is_iter_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_s
return -EINVAL;
for (i = 0; i < nr_slots; i++) {
- struct bpf_stack_state *slot = &state->stack[spi - i];
+ struct bpf_stack_state *slot = bpf_stack_slot(state, spi - i);
struct bpf_reg_state *st = &slot->spilled_ptr;
if (st->type & PTR_UNTRUSTED)
@@ -1081,7 +1178,7 @@ static int mark_stack_slot_irq_flag(struct bpf_verifier_env *env,
if (id < 0)
return id;
- slot = &state->stack[spi];
+ slot = bpf_stack_slot(state, spi);
st = &slot->spilled_ptr;
__mark_reg_known_zero(st);
@@ -1108,7 +1205,7 @@ static int unmark_stack_slot_irq_flag(struct bpf_verifier_env *env, struct bpf_r
if (spi < 0)
return spi;
- slot = &state->stack[spi];
+ slot = bpf_stack_slot(state, spi);
st = &slot->spilled_ptr;
if (st->irq.kfunc_class != kfunc_class) {
@@ -1177,7 +1274,7 @@ static bool is_irq_flag_reg_valid_uninit(struct bpf_verifier_env *env, struct bp
if (spi < 0)
return false;
- slot = &state->stack[spi];
+ slot = bpf_stack_slot(state, spi);
for (i = 0; i < BPF_REG_SIZE; i++)
if (slot->slot_type[i] == STACK_IRQ_FLAG)
@@ -1196,7 +1293,7 @@ static int is_irq_flag_reg_valid_init(struct bpf_verifier_env *env, struct bpf_r
if (spi < 0)
return -EINVAL;
- slot = &state->stack[spi];
+ slot = bpf_stack_slot(state, spi);
st = &slot->spilled_ptr;
if (!st->id)
@@ -1343,7 +1440,7 @@ static int copy_reference_state(struct bpf_verifier_state *dst, const struct bpf
static int copy_stack_state(struct bpf_func_state *dst, const struct bpf_func_state *src)
{
- size_t n = src->allocated_stack / BPF_REG_SIZE;
+ size_t n = bpf_stack_nr_slots(src);
dst->stack = copy_array(dst->stack, src->stack, n, sizeof(struct bpf_stack_state),
GFP_KERNEL_ACCOUNT);
@@ -1380,9 +1477,9 @@ static int resize_reference_state(struct bpf_verifier_state *state, size_t n)
/* Possibly update state->allocated_stack to be at least size bytes. Also
* possibly update the function's high-water mark in its bpf_subprog_info.
*/
-static int grow_stack_state(struct bpf_verifier_env *env, struct bpf_func_state *state, int size)
+int bpf_grow_stack_state(struct bpf_verifier_env *env, struct bpf_func_state *state, int size)
{
- size_t old_n = state->allocated_stack / BPF_REG_SIZE, n;
+ size_t old_n = bpf_stack_nr_slots(state), n;
/* The stack size is always a multiple of BPF_REG_SIZE. */
size = round_up(size, BPF_REG_SIZE);
@@ -1603,6 +1700,7 @@ static void free_func_state(struct bpf_func_state *state)
{
if (!state)
return;
+ kfree(state->loop_stack);
kfree(state->stack_arg_regs);
kfree(state->stack);
kfree(state);
@@ -1639,6 +1737,10 @@ static int copy_func_state(struct bpf_func_state *dst,
memcpy(dst, src, offsetof(struct bpf_func_state, stack));
/* Instruction accounting is path-local, not part of verifier state. */
dst->insns_subtotal = 0;
+ dst->loop_stack = copy_array(dst->loop_stack, src->loop_stack, src->loop_stack_cnt,
+ sizeof(*src->loop_stack), GFP_KERNEL_ACCOUNT);
+ if (!dst->loop_stack)
+ return -ENOMEM;
return copy_stack_state(dst, src);
}
@@ -1667,6 +1769,7 @@ int bpf_copy_verifier_state(struct bpf_verifier_state *dst_state,
return err;
dst_state->speculative = src->speculative;
dst_state->in_sleepable = src->in_sleepable;
+ dst_state->unwound = src->unwound;
dst_state->curframe = src->curframe;
dst_state->branches = src->branches;
dst_state->parent = src->parent;
@@ -1846,12 +1949,124 @@ static void bpf_diag_record_caller_saved(struct bpf_verifier_env *env,
}
}
+static void reg_step_reset(struct bpf_reg_state *reg)
+{
+ reg->base = 0;
+ reg->step = 1;
+}
+
+static void scalar_step_add(struct bpf_reg_state *dst_reg,
+ const struct bpf_reg_state *a,
+ const struct bpf_reg_state *b)
+{
+ const struct bpf_reg_state *reg;
+ s64 amount, tmp;
+
+ if (tnum_is_const(b->var_off)) {
+ reg = a;
+ amount = (s64)b->var_off.value;
+ } else if (tnum_is_const(a->var_off)) {
+ reg = b;
+ amount = (s64)a->var_off.value;
+ } else {
+ reg_step_reset(dst_reg);
+ return;
+ }
+
+ /*
+ * Let x be a possible signed value of the register,
+ * d = amount, s = reg->step, and b = reg->base.
+ * Then x mod s = b, with s > 0 and 0 <= b < s.
+ * If the addition causes signed underflow, the resulting value
+ * is x + d + 2**64.
+ *
+ * Taking the result modulo s and substituting x mod s = b gives:
+ * (x + d + 2**64) mod s = (b + d + (2**64 mod s)) mod s (1)
+ * Keeping reg->step = s and updating reg->base to (b + d) mod s
+ * requires (1) to equal (b + d) mod s, which holds if and only
+ * if 2**64 mod s = 0.
+ * Signed overflow subtracts 2**64 and has the same requirement.
+ *
+ * Conservatively reset the stride if either wrap is possible.
+ */
+ if (check_add_overflow(reg_smin(reg), amount, &tmp) ||
+ check_add_overflow(reg_smax(reg), amount, &tmp)) {
+ reg_step_reset(dst_reg);
+ return;
+ }
+
+ dst_reg->base = (reg->base + imod(amount, reg->step)) % reg->step;
+ dst_reg->step = reg->step;
+}
+
+static void scalar_step_scale(struct bpf_reg_state *dst_reg, u64 amount)
+{
+ u16 step;
+ s64 tmp;
+
+ /*
+ * Using the same notation as scalar_step_add(), with d > 0,
+ * the scaled value satisfies (x * d) mod (s * d) = b * d.
+ * If the multiplication wraps, its signed result can be represented
+ * as x * d - n * 2**64 for some nonzero integer n.
+ *
+ * Taking the result modulo s * d gives:
+ * (x * d - n * 2**64) mod (s * d) =
+ * (b * d - (n * 2**64 mod (s * d))) mod (s * d) (1)
+ * Updating dst_reg->base to b * d and dst_reg->step to s * d
+ * requires (1) to equal b * d, which holds if and only if
+ * n * 2**64 mod (s * d) = 0.
+ *
+ * Conservatively reset the stride if a wrap is possible.
+ */
+ if (amount == 0 || check_mul_overflow(dst_reg->step, amount, &step) ||
+ check_mul_overflow(reg_smin(dst_reg), (s64)amount, &tmp) ||
+ check_mul_overflow(reg_smax(dst_reg), (s64)amount, &tmp)) {
+ reg_step_reset(dst_reg);
+ return;
+ }
+ /*
+ * The above established that `step = dst_reg->step * amount`
+ * does not overflow, therefore the base < step invariant holds
+ * after assignments below.
+ */
+ dst_reg->base = dst_reg->base * amount;
+ dst_reg->step = step;
+}
+
+/*
+ * Let x be a possible signed value of the register,
+ * s = reg->step, and b = reg->base.
+ * Then x mod s = b, with s > 0 and 0 <= b < s.
+ * Truncating to N bits and then zero- or sign-extending x
+ * is equivalent to computing x + k * 2**N for some integer k.
+ * For example, sign-extending the low 8 bits of x = 255 can
+ * be expressed as 255 - 2**8 = -1.
+ *
+ * Taking the result modulo s and substituting x mod s = b gives:
+ * (x + k * 2**N) mod s = (b + (k * 2**N mod s)) mod s
+ * Keeping reg->base = b and reg->step = s requires this to equal b,
+ * which holds if and only if k * 2**N mod s = 0.
+ * Continuing the example with s = 3 and b = 0, we have 255 mod 3 = 0,
+ * but (-1) mod 3 = 2, so the original base and step no longer hold.
+ *
+ * Conservatively reset the stride unless all possible values fit in
+ * the range where the conversion leaves them unchanged.
+ * Call before updating the register's bounds. Requires 0 < bits < 64.
+ */
+static void reg_step_check_unsigned(struct bpf_reg_state *reg, u32 bits)
+{
+ if (reg_umax(reg) >= (1ULL << bits))
+ reg_step_reset(reg);
+}
+
/* This helper doesn't clear reg->id */
static void ___mark_reg_known(struct bpf_reg_state *reg, u64 imm)
{
reg->var_off = tnum_const(imm);
reg->r64 = cnum64_from_urange(imm, imm);
reg->r32 = cnum32_from_urange((u32)imm, (u32)imm);
+ reg_step_reset(reg);
}
/* Mark the unknown part of a register (variable offset or scalar value) as
@@ -1868,6 +2083,12 @@ static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)
___mark_reg_known(reg, imm);
}
+void bpf_mark_reg_known_scalar(struct bpf_reg_state *reg, u64 imm)
+{
+ __mark_reg_known(reg, imm);
+ reg->type = SCALAR_VALUE;
+}
+
static void __mark_reg32_known(struct bpf_reg_state *reg, u64 imm)
{
reg->var_off = tnum_const_subreg(reg->var_off, imm);
@@ -2109,8 +2330,24 @@ static void __reg_bound_offset(struct bpf_reg_state *reg)
struct tnum var32_off = tnum_intersect(tnum_subreg(var64_off),
tnum_range(reg_u32_min(reg),
reg_u32_max(reg)));
+ u32 trailing_zero_bits;
+ u16 base = reg->base;
+ u16 step = reg->step;
reg->var_off = tnum_or(tnum_clear_subreg(var64_off), var32_off);
+
+ if (base == 0)
+ trailing_zero_bits = count_trailing_zeros(step);
+ else
+ trailing_zero_bits = min(count_trailing_zeros(base),
+ count_trailing_zeros(step));
+ reg->var_off = tnum_and(reg->var_off, tnum_const(~0ULL << trailing_zero_bits));
+}
+
+static void __update_bounds_from_step(struct bpf_reg_state *reg)
+{
+ reg->r64 = cnum64_intersect_linear(reg->r64, reg->base, reg->step);
+ deduce_bounds_32_from_64(reg);
}
static bool range_bounds_violation(struct bpf_reg_state *reg);
@@ -2132,6 +2369,15 @@ static void reg_bounds_sync(struct bpf_reg_state *reg)
* then new var_off is (0; 0x7f...fc) which improves our umax.
*/
__update_reg_bounds(reg);
+ /*
+ * Both tnum and base+step define a sequence of points,
+ * potentially w/o intersection, or intersecting after N
+ * tnum_step()'s, where N can be large. On the other hand,
+ * step+base *must* be in sync with r64 in order for
+ * array traversal logic starting at reg_smin() to be sound.
+ * Hence, place this as a final step in the reg_bounds_sync().
+ */
+ __update_bounds_from_step(reg);
}
static bool const_tnum_range_mismatch(struct bpf_reg_state *reg)
@@ -2175,17 +2421,24 @@ static int reg_bounds_sanity_check(struct bpf_verifier_env *env,
goto out;
}
+ if (reg->step == 0 || reg->base >= reg->step) {
+ msg = "invalid base/step";
+ goto out;
+ }
+
return 0;
out:
verifier_bug(env, "REG INVARIANTS VIOLATION (%s): %s r64={.base=%#llx, .size=%#llx} "
- "r32={.base=%#x, .size=%#x} var_off=(%#llx, %#llx)",
+ "r32={.base=%#x, .size=%#x} var_off=(%#llx, %#llx) step=%u+%u",
ctx, msg,
reg->r64.base, reg->r64.size,
reg->r32.base, reg->r32.size,
- reg->var_off.value, reg->var_off.mask);
+ reg->var_off.value, reg->var_off.mask,
+ reg->base, reg->step);
if (env->test_reg_invariants)
return -EFAULT;
__mark_reg_unbounded(reg);
+ reg_step_reset(reg);
return 0;
}
@@ -2196,6 +2449,7 @@ void bpf_mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
reg->type = SCALAR_VALUE;
reg->var_off = tnum_unknown;
__mark_reg_unbounded(reg);
+ reg_step_reset(reg);
}
/* Mark a register as having a completely unknown (scalar) value,
@@ -2406,6 +2660,7 @@ static int add_subprog(struct bpf_verifier_env *env, int off)
return -E2BIG;
}
/* determine subprog starts. The end is one before the next starts */
+ env->subprog_info[env->subprog_cnt].exit_idx = U32_MAX;
env->subprog_info[env->subprog_cnt++].start = off;
sort(env->subprog_info, env->subprog_cnt,
sizeof(env->subprog_info[0]), cmp_subprogs, NULL);
@@ -2514,17 +2769,21 @@ find_kfunc_desc(const struct bpf_prog *prog, u32 func_id, u16 offset)
struct bpf_kfunc_desc_tab *tab;
tab = prog->aux->kfunc_tab;
- return bsearch(&desc, tab->descs, tab->nr_descs,
+ return bsearch(&desc, tab->descs, tab->nr_base_descs,
sizeof(tab->descs[0]), kfunc_desc_cmp_by_id_off);
}
int bpf_get_kfunc_addr(const struct bpf_prog *prog, u32 func_id,
- u16 btf_fd_idx, u8 **func_addr)
+ u16 desc_idx, u8 **func_addr)
{
+ struct bpf_kfunc_desc_tab *tab;
const struct bpf_kfunc_desc *desc;
- desc = find_kfunc_desc(prog, func_id, btf_fd_idx);
- if (!desc)
+ tab = prog->aux->kfunc_tab;
+ if (desc_idx >= tab->nr_descs)
+ return -EFAULT;
+ desc = &tab->descs[desc_idx];
+ if (desc->func_id != func_id)
return -EFAULT;
*func_addr = (u8 *)desc->addr;
@@ -2799,7 +3058,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
}
static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- struct bpf_func_proto *proto);
+ const struct btf_func_model *fm, struct bpf_func_proto *proto);
int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
{
@@ -2864,10 +3123,15 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
if (find_kfunc_desc(env->prog, func_id, offset))
return 0;
- if (tab->nr_descs == MAX_KFUNC_DESCS) {
+ if (tab->nr_base_descs == MAX_KFUNC_DESCS) {
verbose(env, "too many different kernel function calls\n");
return -E2BIG;
}
+ if (tab->nr_descs != tab->nr_base_descs) {
+ verifier_bug(env, "unexpected specialized func desc found (%d descs, %d base descs)",
+ tab->nr_descs, tab->nr_base_descs);
+ return -EFAULT;
+ }
err = fetch_kfunc_meta(env, func_id, offset, &kfunc);
if (err)
@@ -2888,6 +3152,18 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
err = btf_distill_func_proto(&env->log, kfunc.btf, kfunc.proto, kfunc.name, &func_model);
if (err)
return err;
+ if (func_model.ret_size > 8) {
+ if (kfunc.flags && (*kfunc.flags & KF_FASTCALL)) {
+ verbose(env, "kfunc %s with >8-byte return is not supported with KF_FASTCALL\n",
+ kfunc.name);
+ return -EOPNOTSUPP;
+ }
+ if (!bpf_jit_supports_kfunc_ret_reg_pair()) {
+ verbose(env, "kfunc %s with >8-byte return is not supported by JIT\n",
+ kfunc.name);
+ return -EOPNOTSUPP;
+ }
+ }
memset(&meta, 0, sizeof(meta));
meta.btf = kfunc.btf;
@@ -2904,7 +3180,7 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
desc = &tab->descs[tab->nr_descs];
memset(desc, 0, sizeof(*desc));
- err = gen_kfunc_arg_proto(env, &meta, &desc->proto);
+ err = gen_kfunc_arg_proto(env, &meta, &func_model, &desc->proto);
if (err)
return err;
@@ -2913,7 +3189,8 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
desc->addr = addr;
desc->func_model = func_model;
tab->nr_descs++;
- sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
+ tab->nr_base_descs++;
+ sort(tab->descs, tab->nr_base_descs, sizeof(tab->descs[0]),
kfunc_desc_cmp_by_id_off, NULL);
return 0;
}
@@ -2955,6 +3232,22 @@ static int add_subprogs(struct bpf_verifier_env *env)
return ret;
}
+ /*
+ * func_info describes all functions of the program. Those that are
+ * referenced only from data, e.g. from a table of functions to be
+ * called via callx, are not seen by the loop above. They are possible
+ * callees that have to be known upfront as well.
+ */
+ if (env->bpf_capable) {
+ struct bpf_prog_aux *aux = env->prog->aux;
+
+ for (i = 1; i < aux->func_info_cnt; i++) {
+ ret = add_subprog(env, aux->func_info[i].insn_off);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
ret = bpf_find_exception_callback_insn_off(env);
if (ret < 0)
return ret;
@@ -3014,6 +3307,34 @@ static int add_kfuncs(struct bpf_verifier_env *env)
return 0;
}
+static void find_subprog_properties(struct bpf_verifier_env *env)
+{
+ struct bpf_subprog_info *subprog = env->subprog_info;
+ struct bpf_insn *insn = env->prog->insnsi;
+ int cur_subprog;
+
+ for (cur_subprog = 0; cur_subprog < env->subprog_cnt; cur_subprog++) {
+ int i;
+
+ for (i = subprog[cur_subprog].start;
+ i < subprog[cur_subprog + 1].start; i++) {
+ u8 code = insn[i].code;
+
+ if (code == (BPF_JMP | BPF_CALL) &&
+ insn[i].src_reg == 0 &&
+ insn[i].imm == BPF_FUNC_tail_call) {
+ subprog[cur_subprog].has_tail_call = true;
+ subprog[cur_subprog].tail_call_reachable = true;
+ }
+ if (BPF_CLASS(code) == BPF_LD &&
+ (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND))
+ subprog[cur_subprog].has_ld_abs = true;
+ if (bpf_is_callx(&insn[i]))
+ env->has_callx = true;
+ }
+ }
+}
+
static int check_subprogs(struct bpf_verifier_env *env)
{
int i, subprog_start, subprog_end, off, cur_subprog = 0;
@@ -3027,15 +3348,6 @@ static int check_subprogs(struct bpf_verifier_env *env)
for (i = 0; i < insn_cnt; i++) {
u8 code = insn[i].code;
- if (code == (BPF_JMP | BPF_CALL) &&
- insn[i].src_reg == 0 &&
- insn[i].imm == BPF_FUNC_tail_call) {
- subprog[cur_subprog].has_tail_call = true;
- subprog[cur_subprog].tail_call_reachable = true;
- }
- if (BPF_CLASS(code) == BPF_LD &&
- (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND))
- subprog[cur_subprog].has_ld_abs = true;
if (BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32)
goto next;
if (BPF_OP(code) == BPF_CALL)
@@ -3059,9 +3371,10 @@ static int check_subprogs(struct bpf_verifier_env *env)
}
next:
if (i == subprog_end - 1) {
- /* to avoid fall-through from one subprog into another
+ /*
+ * To avoid fall-through from one subprog into another,
* the last insn of the subprog should be either exit
- * or unconditional jump back or bpf_throw call
+ * or unconditional jump back or bpf_throw call.
*/
if (code != (BPF_JMP | BPF_EXIT) &&
code != (BPF_JMP32 | BPF_JA) &&
@@ -3085,11 +3398,89 @@ next:
}
/*
+ * The callee of callx is known to the main verification pass only, which
+ * records the 'caller' -> 'callee' edge of the call graph for the checks
+ * that follow it: absence of recursion and the maximum stack depth.
+ */
+static int record_callx_edge(struct bpf_verifier_env *env, int caller, int callee)
+{
+ u32 cnt = env->subprog_cnt;
+
+ if (!env->callx_edges) {
+ env->callx_edges = kvcalloc(BITS_TO_LONGS(cnt * cnt), sizeof(long),
+ GFP_KERNEL_ACCOUNT);
+ if (!env->callx_edges)
+ return -ENOMEM;
+ }
+ __set_bit(caller * cnt + callee, env->callx_edges);
+ return 0;
+}
+
+/*
+ * Return the first subprog with the number >= 'from' that 'caller' calls
+ * via callx, or -1 when there is none.
+ */
+static int next_callx_callee(struct bpf_verifier_env *env, int caller, int from)
+{
+ u32 cnt = env->subprog_cnt;
+ unsigned long bit, end = (caller + 1) * cnt;
+
+ if (!env->callx_edges || from >= cnt)
+ return -1;
+ bit = find_next_bit(env->callx_edges, end, caller * cnt + from);
+ return bit < end ? bit - caller * cnt : -1;
+}
+
+/*
+ * 'call foo; unwind_goto pad' goes to the pad when foo left with unwind_exit.
+ * JITs don't emit a branch for it. unwind_exit finds the pad by its return
+ * address. So unwind_goto has to follow the call and nothing may jump to it.
+ */
+static int check_unwind_insns(struct bpf_verifier_env *env)
+{
+ struct bpf_insn *insn = env->prog->insnsi;
+ bool seen = false;
+ int i;
+
+ for (i = 0; i < env->prog->len; i++, insn++) {
+ if (bpf_is_unwind_exit(insn)) {
+ bpf_find_containing_subprog(env, i)->can_unwind = true;
+ seen = true;
+ }
+ if (!bpf_is_unwind_goto(insn))
+ continue;
+ seen = true;
+ if (!i || !(bpf_pseudo_call(insn - 1) || bpf_is_callx(insn - 1))) {
+ verbose(env, "unwind_goto at insn %d doesn't follow a call of a subprog\n",
+ i);
+ return -EINVAL;
+ }
+ if (bpf_is_jump_target(env, i)) {
+ verbose(env, "jump to unwind_goto at insn %d\n", i);
+ return -EINVAL;
+ }
+ }
+ if (!seen)
+ return 0;
+ if (!bpf_jit_supports_unwind() || bpf_prog_is_offloaded(env->prog->aux)) {
+ verbose(env, "JIT does not support unwinding\n");
+ return -ENOTSUPP;
+ }
+ env->prog->jit_required = 1;
+ env->prog->aux->can_unwind = env->subprog_info[0].can_unwind;
+ return 0;
+}
+
+/*
* Sort subprogs in topological order so that leaf subprogs come first and
* their callers come later. This is a DFS post-order traversal of the call
* graph. Scan only reachable instructions (those in the computed postorder) of
* the current subprog to discover callees (direct subprogs and sync
* callbacks).
+ *
+ * The callees of callx are not known before the main verification pass.
+ * When callx is used the sort is repeated after it with the recorded callx
+ * edges added to the call graph to reject recursion through indirect calls.
*/
static int sort_subprogs_topo(struct bpf_verifier_env *env)
{
@@ -3130,12 +3521,22 @@ static int sort_subprogs_topo(struct bpf_verifier_env *env)
int idx = insn_postorder[j];
int callee;
- if (!bpf_pseudo_call(&insn[idx]) && !bpf_pseudo_func(&insn[idx]))
+ if (bpf_is_callx(&insn[idx])) {
+ /* find a callee that is not explored yet */
+ callee = -1;
+ do {
+ callee = next_callx_callee(env, cur, callee + 1);
+ } while (callee >= 0 && color[callee] == 2);
+ if (callee < 0)
+ continue;
+ } else if (bpf_pseudo_call(&insn[idx]) || bpf_pseudo_func(&insn[idx])) {
+ callee = bpf_find_subprog(env, idx + insn[idx].imm + 1);
+ if (callee < 0) {
+ ret = -EFAULT;
+ goto out;
+ }
+ } else {
continue;
- callee = bpf_find_subprog(env, idx + insn[idx].imm + 1);
- if (callee < 0) {
- ret = -EFAULT;
- goto out;
}
if (color[callee] == 2)
continue;
@@ -3232,26 +3633,30 @@ static void mark_indirect_target(struct bpf_verifier_env *env, int idx)
env->insn_aux_data[idx].indirect_target = true;
}
+static void mark_non_stack_access(struct bpf_verifier_env *env, int idx)
+{
+ env->insn_aux_data[idx].non_stack_access = true;
+}
+
+/* Layout of one packed linked register in the jump history, see linked_regs_pack() */
#define LR_FRAMENO_BITS 4
-#define LR_SPI_BITS 6
-#define LR_ENTRY_BITS (LR_SPI_BITS + LR_FRAMENO_BITS + 1)
-#define LR_SIZE_BITS 4
-#define LR_FRAMENO_MASK ((1ull << LR_FRAMENO_BITS) - 1)
-#define LR_SPI_MASK ((1ull << LR_SPI_BITS) - 1)
-#define LR_SIZE_MASK ((1ull << LR_SIZE_BITS) - 1)
-#define LR_SPI_OFF LR_FRAMENO_BITS
-#define LR_IS_REG_OFF (LR_SPI_BITS + LR_FRAMENO_BITS)
-#define LINKED_REGS_MAX 5
+#define LR_INDEX_BITS 11
+#define LR_FRAMENO_MASK ((1u << LR_FRAMENO_BITS) - 1)
+#define LR_IS_REG BIT(LR_FRAMENO_BITS)
+#define LR_INDEX_OFF (LR_FRAMENO_BITS + 1)
+#define LR_INDEX_MASK ((1u << LR_INDEX_BITS) - 1)
+#define LINKED_REGS_MAX BPF_LINKED_REGS_MAX
static_assert(MAX_CALL_FRAMES <= (1 << LR_FRAMENO_BITS));
-static_assert(LINKED_REGS_MAX < (1 << LR_SIZE_BITS));
-static_assert(LINKED_REGS_MAX * LR_ENTRY_BITS + LR_SIZE_BITS <= 64);
+static_assert(MAX_BPF_REG <= (1 << LR_INDEX_BITS));
+static_assert(MAX_BPF_STACK_SLOTS <= (1 << LR_INDEX_BITS));
+static_assert(LR_INDEX_OFF + LR_INDEX_BITS <= 16);
struct linked_reg {
u8 frameno;
union {
- u8 spi;
- u8 regno;
+ u16 spi;
+ u16 regno;
};
bool is_reg;
};
@@ -3270,48 +3675,34 @@ static struct linked_reg *linked_regs_push(struct linked_regs *s)
}
/*
- * Use u64 as a vector of 5 11-bit values, use first 4-bits to track
- * number of elements currently in stack.
- * Pack one history entry for linked registers as 11 bits in the following format:
- * - 4-bits frameno
- * - 6-bits spi_or_reg
- * - 1-bit is_reg
+ * Pack linked registers for a jump history entry, one u16 each:
+ * - 4 bits frameno
+ * - 1 bit is_reg
+ * - 11 bits register or stack slot index
*/
-static u64 linked_regs_pack(struct linked_regs *s)
+static void linked_regs_pack(const struct linked_regs *s, u16 *packed)
{
- u64 val = 0;
int i;
for (i = 0; i < s->cnt; ++i) {
- struct linked_reg *e = &s->entries[i];
- u64 tmp = 0;
-
- tmp |= e->frameno;
- tmp |= e->spi << LR_SPI_OFF;
- tmp |= (e->is_reg ? 1 : 0) << LR_IS_REG_OFF;
+ const struct linked_reg *e = &s->entries[i];
- val <<= LR_ENTRY_BITS;
- val |= tmp;
+ packed[i] = e->frameno | (e->is_reg ? LR_IS_REG : 0) | (e->spi << LR_INDEX_OFF);
}
- val <<= LR_SIZE_BITS;
- val |= s->cnt;
- return val;
}
-static void linked_regs_unpack(u64 val, struct linked_regs *s)
+static void linked_regs_unpack(const struct bpf_jmp_history_entry *hist, struct linked_regs *s)
{
int i;
- s->cnt = val & LR_SIZE_MASK;
- val >>= LR_SIZE_BITS;
-
+ s->cnt = hist->linked_regs_cnt;
for (i = 0; i < s->cnt; ++i) {
struct linked_reg *e = &s->entries[i];
+ u16 packed = hist->linked_regs[i];
- e->frameno = val & LR_FRAMENO_MASK;
- e->spi = (val >> LR_SPI_OFF) & LR_SPI_MASK;
- e->is_reg = (val >> LR_IS_REG_OFF) & 0x1;
- val >>= LR_ENTRY_BITS;
+ e->frameno = packed & LR_FRAMENO_MASK;
+ e->is_reg = packed & LR_IS_REG;
+ e->spi = (packed >> LR_INDEX_OFF) & LR_INDEX_MASK;
}
}
@@ -3353,10 +3744,10 @@ void bpf_bt_sync_linked_regs(struct backtrack_state *bt, struct bpf_jmp_history_
bool some_precise = false;
int i;
- if (!hist || hist->linked_regs == 0)
+ if (!hist || !hist->linked_regs_cnt)
return;
- linked_regs_unpack(hist->linked_regs, &linked_regs);
+ linked_regs_unpack(hist, &linked_regs);
for (i = 0; i < linked_regs.cnt; ++i) {
struct linked_reg *e = &linked_regs.entries[i];
@@ -3453,17 +3844,18 @@ static void save_register_state(struct bpf_verifier_env *env,
int spi, struct bpf_reg_state *reg,
int size)
{
+ struct bpf_stack_state *slot = bpf_stack_slot(state, spi);
int i;
- bpf_diag_mod_begin(env, &state->stack[spi].spilled_ptr, reg, BPF_DIAG_MOD_SPILL);
- state->stack[spi].spilled_ptr = *reg;
+ bpf_diag_mod_begin(env, &slot->spilled_ptr, reg, BPF_DIAG_MOD_SPILL);
+ slot->spilled_ptr = *reg;
for (i = BPF_REG_SIZE; i > BPF_REG_SIZE - size; i--)
- state->stack[spi].slot_type[i - 1] = STACK_SPILL;
+ slot->slot_type[i - 1] = STACK_SPILL;
/* size < 8 bytes spill */
for (; i; i--)
- mark_stack_slot_misc(env, &state->stack[spi].slot_type[i - 1]);
+ mark_stack_slot_misc(env, &slot->slot_type[i - 1]);
bpf_diag_mod_end(env);
}
@@ -3505,14 +3897,15 @@ static void check_fastcall_stack_contract(struct bpf_verifier_env *env,
static void scrub_special_slot(struct bpf_func_state *state, int spi)
{
+ struct bpf_stack_state *slot = bpf_stack_slot(state, spi);
int i;
/* regular write of data into stack destroys any spilled ptr */
- state->stack[spi].spilled_ptr.type = NOT_INIT;
+ slot->spilled_ptr.type = NOT_INIT;
/* Mark slots as STACK_MISC if they belonged to spilled ptr/dynptr/iter. */
- if (is_stack_slot_special(&state->stack[spi]))
+ if (is_stack_slot_special(slot))
for (i = 0; i < BPF_REG_SIZE; i++)
- scrub_spilled_slot(&state->stack[spi].slot_type[i]);
+ scrub_spilled_slot(&slot->slot_type[i]);
}
/* check_stack_{read,write}_fixed_off functions track spill/fill of registers,
@@ -3530,13 +3923,15 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
struct bpf_reg_state *reg = NULL;
int insn_flags = INSN_F_STACK_ACCESS;
int hist_spi = spi, hist_frame = state->frameno;
+ struct bpf_stack_state *ss = bpf_stack_slot(state, spi);
- /* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0,
+ /*
+ * caller checked that off % size == 0 and -env->stack_limit <= off < 0,
* so it's aligned access and [off, off + size) are within stack limits
*/
if (!env->allow_ptr_leaks &&
- bpf_is_spilled_reg(&state->stack[spi]) &&
- !bpf_is_spilled_scalar_reg(&state->stack[spi]) &&
+ bpf_is_spilled_reg(ss) &&
+ !bpf_is_spilled_scalar_reg(ss) &&
size != BPF_REG_SIZE) {
const char *reason;
@@ -3558,7 +3953,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
bool sanitize = reg && is_pointer_regtype(reg->type);
for (i = 0; i < size; i++) {
- u8 type = state->stack[spi].slot_type[(slot - i) %
+ u8 type = ss->slot_type[(slot - i) %
BPF_REG_SIZE];
if (type != STACK_MISC && type != STACK_ZERO) {
@@ -3575,7 +3970,6 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
if (err)
return err;
- check_fastcall_stack_contract(env, state, insn_idx, off);
mark_stack_slot_scratched(env, spi);
if (reg && !(off % BPF_REG_SIZE) && reg->type == SCALAR_VALUE && env->bpf_capable) {
bool reg_value_fits;
@@ -3587,7 +3981,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
save_register_state(env, state, spi, reg, size);
/* Break the relation on a narrowing spill. */
if (!reg_value_fits)
- clear_scalar_id(&state->stack[spi].spilled_ptr);
+ clear_scalar_id(&ss->spilled_ptr);
} else if (!reg && !(off % BPF_REG_SIZE) && is_bpf_st_mem(insn) &&
env->bpf_capable) {
struct bpf_reg_state *tmp_reg = &env->fake_reg[0];
@@ -3611,8 +4005,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
} else {
u8 type = STACK_MISC;
- if (bpf_is_spilled_reg(&state->stack[spi]))
- bpf_diag_record_scrub(env, &state->stack[spi].spilled_ptr,
+ if (bpf_is_spilled_reg(ss))
+ bpf_diag_record_scrub(env, &ss->spilled_ptr,
BPF_DIAG_MOD_WRITE);
scrub_special_slot(state, spi);
@@ -3633,13 +4027,13 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
/* Mark slots affected by this stack write. */
for (i = 0; i < size; i++)
- state->stack[spi].slot_type[(slot - i) % BPF_REG_SIZE] = type;
+ ss->slot_type[(slot - i) % BPF_REG_SIZE] = type;
insn_flags = 0; /* not a register spill */
}
if (insn_flags)
return bpf_push_jmp_history(env, env->cur_state, insn_flags,
- hist_spi, hist_frame, 0);
+ hist_spi, hist_frame, NULL, 0);
return 0;
}
@@ -3687,24 +4081,21 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,
(!value_reg && is_bpf_st_mem(insn) && insn->imm == 0))
writing_zero = true;
- for (i = min_off; i < max_off; i++) {
- int spi;
-
- spi = bpf_get_spi(i);
- err = destroy_if_dynptr_stack_slot(env, state, spi);
- if (err)
- return err;
- }
+ err = destroy_dynptrs_in_stack_slots(env, state, bpf_get_spi(max_off - 1),
+ bpf_get_spi(min_off));
+ if (err)
+ return err;
- check_fastcall_stack_contract(env, state, insn_idx, min_off);
/* Variable offset writes destroy any spilled pointers in range. */
for (i = min_off; i < max_off; i++) {
+ struct bpf_stack_state *ss;
u8 new_type, *stype;
int slot, spi;
slot = -i - 1;
spi = slot / BPF_REG_SIZE;
- stype = &state->stack[spi].slot_type[slot % BPF_REG_SIZE];
+ ss = bpf_stack_slot(state, spi);
+ stype = &ss->slot_type[slot % BPF_REG_SIZE];
mark_stack_slot_scratched(env, spi);
if (!env->allow_ptr_leaks && *stype != STACK_MISC && *stype != STACK_ZERO) {
@@ -3727,9 +4118,8 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,
/* If writing_zero and the spi slot contains a spill of value 0,
* maintain the spill type.
*/
- if (writing_zero && *stype == STACK_SPILL &&
- bpf_is_spilled_scalar_reg(&state->stack[spi])) {
- struct bpf_reg_state *spill_reg = &state->stack[spi].spilled_ptr;
+ if (writing_zero && *stype == STACK_SPILL && bpf_is_spilled_scalar_reg(ss)) {
+ struct bpf_reg_state *spill_reg = &ss->spilled_ptr;
if (tnum_is_const(spill_reg->var_off) && spill_reg->var_off.value == 0) {
zero_used = true;
@@ -3800,23 +4190,22 @@ static int mark_reg_stack_read(struct bpf_verifier_env *env,
{
struct bpf_verifier_state *vstate = env->cur_state;
struct bpf_func_state *state = vstate->frame[vstate->curframe];
- u64 zero_spill_mask = 0;
int i, slot, spi;
u8 *stype;
- int zeros = 0;
+ int zeros = 0, zero_spills = 0;
for (i = min_off; i < max_off; i++) {
slot = -i - 1;
spi = slot / BPF_REG_SIZE;
mark_stack_slot_scratched(env, spi);
- stype = ptr_state->stack[spi].slot_type;
+ stype = bpf_stack_slot(ptr_state, spi)->slot_type;
if (stype[slot % BPF_REG_SIZE] == STACK_ZERO) {
zeros++;
continue;
}
if (stype[slot % BPF_REG_SIZE] == STACK_SPILL &&
- bpf_register_is_null(&ptr_state->stack[spi].spilled_ptr)) {
- zero_spill_mask |= 1ull << spi;
+ bpf_register_is_null(&bpf_stack_slot(ptr_state, spi)->spilled_ptr)) {
+ zero_spills++;
zeros++;
continue;
}
@@ -3827,8 +4216,14 @@ static int mark_reg_stack_read(struct bpf_verifier_env *env,
* so the whole register == const_zero.
*/
__mark_reg_const_zero(env, &state->regs[dst_regno]);
- if (zero_spill_mask) {
- bpf_bt_set_frame_slot_mask(&env->bt, ptr_state->frameno, zero_spill_mask);
+ if (zero_spills) {
+ for (i = min_off; i < max_off; i++) {
+ slot = -i - 1;
+ spi = slot / BPF_REG_SIZE;
+ stype = bpf_stack_slot(ptr_state, spi)->slot_type;
+ if (stype[slot % BPF_REG_SIZE] == STACK_SPILL)
+ bpf_bt_set_frame_slot(&env->bt, ptr_state->frameno, spi);
+ }
return mark_chain_precision_batch(env, env->cur_state);
}
} else {
@@ -3839,19 +4234,46 @@ static int mark_reg_stack_read(struct bpf_verifier_env *env,
return 0;
}
-static void bpf_diag_stack_read_uninit(struct bpf_verifier_env *env, int off, int i,
- int size)
+static void bpf_diag_stack_read_invalid(struct bpf_verifier_env *env, int off, int i, int size,
+ enum bpf_stack_slot_type type)
{
- const char *reason;
+ const char *problem, *reason, *suggestion, *kind;
+
+ if (type == STACK_INVALID) {
+ reason = bpf_diag_fmt(
+ env, "This rejected read uses %d bytes at stack offset %d, but byte %d in that range is uninitialized on this path. "
+ "Programs loaded with CAP_PERFMON can be allowed to read uninitialized stack bytes, but this program is being rejected without that allowance.",
+ size, off, i);
+ bpf_diag_memory(
+ env, env->insn_idx, "uninitialized stack read", reason,
+ "Initialize every byte in the stack range before reading it, adjust the offset and size so the read covers only initialized bytes, "
+ "or load with CAP_PERFMON if uninitialized stack reads are intended.");
+ return;
+ }
+
+ switch (type) {
+ case STACK_DYNPTR:
+ kind = "dynptr";
+ suggestion = "Use dynptr helpers or kfuncs to access the object represented by the dynptr instead of reading the dynptr state directly.";
+ break;
+ case STACK_ITER:
+ kind = "iterator";
+ suggestion = "Use iterator kfuncs to advance or destroy the iterator instead of reading its state directly.";
+ break;
+ case STACK_IRQ_FLAG:
+ kind = "IRQ flag";
+ suggestion = "Pass the saved IRQ flag to the matching restore kfunc instead of reading its state directly.";
+ break;
+ default:
+ return;
+ }
- reason = bpf_diag_fmt(env,
- "This rejected read uses %d bytes at stack offset %d, but byte %d in that range is uninitialized on this path. "
- "Programs loaded with CAP_PERFMON can be allowed to read uninitialized stack bytes, but this program is being rejected without that allowance.",
- size, off, i);
- bpf_diag_memory(
- env, env->insn_idx, "uninitialized stack read", reason,
- "Initialize every byte in the stack range before reading it, adjust the offset and size so the read covers only initialized bytes, "
- "or load with CAP_PERFMON if uninitialized stack reads are intended.");
+ problem = bpf_diag_fmt(env, "direct read of %s stack state", kind);
+ reason = bpf_diag_fmt(
+ env, "This rejected read uses %d bytes at stack offset %d, but byte %d in that range belongs to verifier-managed %s state. "
+ "This state has an opaque representation that BPF programs cannot read directly.",
+ size, off, i, kind);
+ bpf_diag_memory(env, env->insn_idx, problem, reason, suggestion);
}
/* Read the stack at 'off' and put the results into the register indicated by
@@ -3876,12 +4298,12 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
int err;
int insn_flags = INSN_F_STACK_ACCESS;
int hist_spi = spi, hist_frame = reg_state->frameno;
+ struct bpf_stack_state *ss = bpf_stack_slot(reg_state, spi);
- stype = reg_state->stack[spi].slot_type;
- reg = &reg_state->stack[spi].spilled_ptr;
+ stype = ss->slot_type;
+ reg = &ss->spilled_ptr;
mark_stack_slot_scratched(env, spi);
- check_fastcall_stack_contract(env, state, env->insn_idx, off);
/*
* Refine the in-progress load record's origin to the source stack slot.
@@ -3889,11 +4311,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
if (dst_regno >= 0)
bpf_diag_mod_begin(env, &state->regs[dst_regno], reg, BPF_DIAG_MOD_WRITE);
- if (bpf_is_spilled_reg(&reg_state->stack[spi])) {
- u8 spill_size = 1;
-
- for (i = BPF_REG_SIZE - 1; i > 0 && stype[i - 1] == STACK_SPILL; i--)
- spill_size++;
+ if (bpf_is_spilled_reg(ss)) {
+ u8 spill_size = bpf_spill_size(ss);
if (size != BPF_REG_SIZE || spill_size != BPF_REG_SIZE) {
if (reg->type != SCALAR_VALUE) {
@@ -3902,11 +4321,11 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
return -EACCES;
}
- if (dst_regno < 0)
- return 0;
-
if (size <= spill_size &&
bpf_stack_narrow_access_ok(off, size, spill_size)) {
+ if (dst_regno < 0)
+ return 0;
+
if (env->bpf_capable && size == 4 && spill_size == 4 &&
get_reg_width(reg) <= 32)
/* Ensure stack slot has an ID to build a relation
@@ -3943,11 +4362,14 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
} else {
verbose(env, "invalid read from stack off %d+%d size %d\n",
off, i, size);
- bpf_diag_stack_read_uninit(env, off, i, size);
+ bpf_diag_stack_read_invalid(env, off, i, size, type);
}
return -EACCES;
}
+ if (dst_regno < 0)
+ return 0;
+
if (spill_cnt == size &&
tnum_is_const(reg->var_off) && reg->var_off.value == 0) {
__mark_reg_const_zero(env, &state->regs[dst_regno]);
@@ -4002,7 +4424,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
} else {
verbose(env, "invalid read from stack off %d+%d size %d\n",
off, i, size);
- bpf_diag_stack_read_uninit(env, off, i, size);
+ bpf_diag_stack_read_invalid(env, off, i, size, type);
}
return -EACCES;
}
@@ -4015,7 +4437,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
}
if (insn_flags)
return bpf_push_jmp_history(env, env->cur_state, insn_flags,
- hist_spi, hist_frame, 0);
+ hist_spi, hist_frame, NULL, 0);
return 0;
}
@@ -4068,7 +4490,6 @@ static int check_stack_read_var_off(struct bpf_verifier_env *env, struct bpf_reg
dst_regno);
if (err)
return err;
- check_fastcall_stack_contract(env, ptr_state, env->insn_idx, min_off);
return 0;
}
@@ -4101,13 +4522,13 @@ static int check_stack_read(struct bpf_verifier_env *env,
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
verbose(env, "variable offset stack pointer cannot be passed into helper function; var_off=%s off=%d size=%d\n",
tn_buf, off, size);
- reason = bpf_diag_fmt(env,
- "The helper would access the stack through variable offset %s plus fixed offset %d and size %d. "
- "Helper stack memory arguments require a constant stack offset and a precise initialized range.",
+ reason = bpf_diag_fmt(
+ env, "The instruction would access the stack through variable offset %s plus fixed offset %d and size %d. "
+ "This stack access requires a constant stack offset and a precise initialized range.",
tn_buf, off, size);
bpf_diag_memory(
- env, env->insn_idx, "variable stack access", reason,
- "Use a fixed stack offset for helper memory arguments, or copy the needed bytes into a fixed stack slot first.");
+ env, env->insn_idx, "variable-offset stack access", reason,
+ "Use a fixed stack offset for the instruction, selecting the target stack slot on separate control-flow paths if necessary.");
return -EACCES;
}
/* Variable offset is prohibited for unprivileged mode for simplicity
@@ -4205,7 +4626,7 @@ static int check_stack_arg_write(struct bpf_verifier_env *env, struct bpf_func_s
bpf_diag_mod_end(env);
state->no_stack_arg_load = true;
return bpf_push_jmp_history(env, env->cur_state,
- INSN_F_STACK_ARG_ACCESS, spi, 0, 0);
+ INSN_F_STACK_ARG_ACCESS, spi, 0, NULL, 0);
}
/*
@@ -4239,7 +4660,7 @@ static int check_stack_arg_read(struct bpf_verifier_env *env, struct bpf_func_st
cur->regs[dst_regno] = *arg;
bpf_diag_mod_end(env);
return bpf_push_jmp_history(env, env->cur_state,
- INSN_F_STACK_ARG_ACCESS, spi, 0, 0);
+ INSN_F_STACK_ARG_ACCESS, spi, 0, NULL, 0);
}
static int mark_stack_arg_precision(struct bpf_verifier_env *env, int arg_idx)
@@ -4599,7 +5020,7 @@ BTF_ID(struct, cgroup)
BTF_ID(struct, bpf_cpumask)
#endif
BTF_ID(struct, task_struct)
-#ifdef CONFIG_CRYPTO
+#ifdef CONFIG_BPF_CRYPTO
BTF_ID(struct, bpf_crypto_ctx)
#endif
#ifdef CONFIG_INET
@@ -4822,7 +5243,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_func_proto *fn,
+ const struct bpf_call_arg_meta *meta,
enum bpf_access_type t)
{
enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
@@ -4846,10 +5267,11 @@ 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 (fn)
- return fn->pkt_access;
+ if (meta && !meta->btf && meta->func_id)
+ return meta->fn->pkt_access;
- env->seen_direct_write = true;
+ if (t == BPF_WRITE)
+ env->seen_direct_write = true;
return true;
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
@@ -5061,6 +5483,19 @@ static bool is_arena_reg(struct bpf_verifier_env *env, int regno)
return reg->type == PTR_TO_ARENA;
}
+/*
+ * There are no address spaces in Rust, addresses of arena are plain numbers.
+ * When the program is loaded with BPF_F_ARENA_SCALAR and has an arena a load or
+ * a store through a number is an access to arena at the low 32 bits of it,
+ * like the access through PTR_TO_ARENA is. The register stays a number.
+ */
+static bool is_arena_scalar(struct bpf_verifier_env *env, int regno)
+{
+ const struct bpf_reg_state *reg = reg_state(env, regno);
+
+ return reg->type == SCALAR_VALUE && env->arena_scalar && env->prog->aux->arena;
+}
+
static bool is_load_acq_unsafe(struct bpf_verifier_env *env, int regno,
struct bpf_insn *insn)
{
@@ -5091,7 +5526,7 @@ static bool atomic_ptr_type_ok(struct bpf_verifier_env *env, int regno,
return false;
if (is_sk_reg(env, regno))
return false;
- if (is_arena_reg(env, regno))
+ if (is_arena_reg(env, regno) || is_arena_scalar(env, regno))
return bpf_jit_supports_insn(insn, true);
if (is_load_acq_unsafe(env, regno, insn))
return false;
@@ -5107,27 +5542,17 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = {
[CONST_PTR_TO_MAP] = btf_bpf_map_id,
};
-static enum bpf_reg_type lookup_reg2btf_ids(u32 ref_id)
-{
- enum bpf_reg_type type;
-
- for (type = 0; type < __BPF_REG_TYPE_MAX; type++) {
- if (reg2btf_ids[type] && *reg2btf_ids[type] == ref_id)
- return type;
- }
-
- return NOT_INIT;
-}
-
-static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg)
+static bool is_trusted_reg_type(struct bpf_verifier_env *env,
+ const struct bpf_reg_state *reg,
+ enum bpf_reg_type type)
{
/* A referenced register is always trusted. */
if (reg_is_referenced(env, reg))
return true;
/* Types listed in the reg2btf_ids are always trusted */
- if (reg2btf_ids[base_type(reg->type)] &&
- !bpf_type_has_unsafe_modifiers(reg->type))
+ if (reg2btf_ids[base_type(type)] &&
+ !bpf_type_has_unsafe_modifiers(type))
return true;
/* If a register is not referenced, it is trusted if it has the
@@ -5139,8 +5564,14 @@ static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_st
* Eventually, we should make PTR_TRUSTED the single source of truth
* for whether a register is trusted.
*/
- return type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS &&
- !bpf_type_has_unsafe_modifiers(reg->type);
+ return type_flag(type) & BPF_REG_TRUSTED_MODIFIERS &&
+ !bpf_type_has_unsafe_modifiers(type);
+}
+
+static bool is_trusted_reg(struct bpf_verifier_env *env,
+ const struct bpf_reg_state *reg)
+{
+ return is_trusted_reg_type(env, reg, reg->type);
}
static bool is_rcu_reg(const struct bpf_reg_state *reg)
@@ -5313,6 +5744,9 @@ struct bpf_subprog_call_depth_info {
int ret_insn; /* caller instruction where we return to. */
int caller; /* caller subprogram idx */
int frame; /* # of consecutive static call stack frames on top of stack */
+ int callx_insn; /* callx instruction whose callees are being walked */
+ int callx_next; /* next callee of callx_insn to walk */
+ bool via_callx; /* the subprogram is entered via callx */
};
/* starting from main bpf function walk all instructions of the function
@@ -5332,11 +5766,13 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
/* no caller idx */
dinfo[idx].caller = -1;
+ dinfo[idx].via_callx = false;
i = subprog[idx].start;
if (!priv_stack_supported)
subprog[idx].priv_stack_mode = NO_PRIV_STACK;
process_func:
+ dinfo[idx].callx_insn = -1;
if (subprog[idx].has_ld_abs) {
for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) {
if (subprog[tmp].is_cb) {
@@ -5389,7 +5825,7 @@ process_func:
if (subprog[idx].priv_stack_mode == PRIV_STACK_ADAPTIVE) {
if (subprog_depth > env->max_stack_depth)
env->max_stack_depth = subprog_depth;
- if (subprog_depth > MAX_BPF_STACK) {
+ if (subprog_depth > env->stack_limit) {
verbose(env, "stack size of subprog %d is %d. Too large\n",
idx, subprog_depth);
return -EACCES;
@@ -5398,7 +5834,7 @@ process_func:
depth += subprog_depth;
if (depth > env->max_stack_depth)
env->max_stack_depth = depth;
- if (depth > MAX_BPF_STACK) {
+ if (depth > env->stack_limit) {
total = 0;
for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller)
total++;
@@ -5413,11 +5849,10 @@ continue_func:
for (; i < subprog_end; i++) {
int next_insn, sidx;
- if (bpf_pseudo_kfunc_call(insn + i) && !insn[i].off) {
+ if (bpf_pseudo_kfunc_call(insn + i) && !insn[i].off &&
+ bpf_is_throw_kfunc(insn + i)) {
bool err = false;
- if (!bpf_is_throw_kfunc(insn + i))
- continue;
for (tmp = idx; tmp >= 0 && !err; tmp = dinfo[tmp].caller) {
if (subprog[tmp].is_cb) {
err = true;
@@ -5432,31 +5867,70 @@ continue_func:
return -EINVAL;
}
- if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i))
+ if (bpf_is_callx(insn + i) || bpf_calls_callback(env, i)) {
+ /*
+ * Walk the callees and the callbacks recorded by
+ * the main verification pass one by one, returning to
+ * this insn after each.
+ */
+ if (dinfo[idx].callx_insn != i) {
+ dinfo[idx].callx_insn = i;
+ dinfo[idx].callx_next = 0;
+ }
+ sidx = next_callx_callee(env, idx, dinfo[idx].callx_next);
+ if (sidx < 0)
+ continue;
+ dinfo[idx].callx_next = sidx + 1;
+ dinfo[idx].ret_insn = i;
+ next_insn = subprog[sidx].start;
+ } else if (bpf_pseudo_call(insn + i) || bpf_pseudo_func(insn + i)) {
+ /* find the callee */
+ next_insn = i + insn[i].imm + 1;
+ sidx = bpf_find_subprog(env, next_insn);
+ if (verifier_bug_if(sidx < 0, env, "callee not found at insn %d", next_insn))
+ return -EFAULT;
+ if (subprog[sidx].is_async_cb) {
+ /* async callbacks don't increase bpf prog stack size unless called directly */
+ if (!bpf_pseudo_call(insn + i))
+ continue;
+ if (subprog[sidx].is_exception_cb) {
+ verbose(env, "insn %d cannot call exception cb directly", i);
+ return -EINVAL;
+ }
+ }
+ /* remember insn to return to */
+ dinfo[idx].ret_insn = i + 1;
+ } else {
continue;
- /* remember insn and function to return to */
+ }
- /* find the callee */
- next_insn = i + insn[i].imm + 1;
- sidx = bpf_find_subprog(env, next_insn);
- if (verifier_bug_if(sidx < 0, env, "callee not found at insn %d", next_insn))
- return -EFAULT;
- if (subprog[sidx].is_async_cb) {
- /* async callbacks don't increase bpf prog stack size unless called directly */
- if (!bpf_pseudo_call(insn + i))
+ /*
+ * sort_subprogs_topo() tolerates cycles in the call graph that
+ * go through the address of a function being taken, since it
+ * doesn't know what it is taken for. Such cycle is a recursion
+ * unless it's an async callback, which are skipped above.
+ * The main verification pass limits the depth of the recursion,
+ * but it doesn't follow calls of global functions.
+ */
+ for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) {
+ if (tmp != sidx)
continue;
- if (subprog[sidx].is_exception_cb) {
- verbose(env, "insn %d cannot call exception cb directly", i);
- return -EINVAL;
- }
+ verbose(env, "recursive call from %s() to %s()\n",
+ bpf_subprog_name(env, idx), bpf_subprog_name(env, sidx));
+ bpf_diag_program_structure(
+ env, i, "recursive subprogram call",
+ "Rewrite the recursion as an explicit bounded loop, or split the logic so subprogram calls do not form a cycle.",
+ "This call or callback would make the subprogram call graph recursive. "
+ "The verifier requires a finite, acyclic call graph so it can bound stack depth and analysis.");
+ return -EINVAL;
}
/* store caller info for after we return from callee */
dinfo[idx].frame = frame;
- dinfo[idx].ret_insn = i + 1;
/* push caller idx into callee's dinfo */
dinfo[sidx].caller = idx;
+ dinfo[sidx].via_callx = bpf_is_callx(insn + i);
i = next_insn;
@@ -5490,6 +5964,14 @@ continue_func:
verbose(env, "tail_calls are not allowed in programs with stack args\n");
return -EINVAL;
}
+ /*
+ * JITs pass tail call counter in a register that is
+ * not available when the callee is called via callx.
+ */
+ if (dinfo[tmp].via_callx) {
+ verbose(env, "tail_calls are not allowed in functions called via callx\n");
+ return -EINVAL;
+ }
subprog[tmp].tail_call_reachable = true;
}
} else if (!idx && subprog[0].has_tail_call && subprog[0].stack_arg_cnt) {
@@ -5625,9 +6107,10 @@ static int check_buffer_access(struct bpf_verifier_env *env,
const struct bpf_reg_state *reg,
argno_t argno, int off, int size,
bool zero_size_allowed,
- u32 *max_access)
+ u32 *max_access, bool write)
{
const char *buf_info = type_is_rdonly_mem(reg->type) ? "rdonly" : "rdwr";
+ struct bpf_prog_aux *aux = env->prog->aux;
u32 access_end;
int err;
@@ -5636,6 +6119,11 @@ static int check_buffer_access(struct bpf_verifier_env *env,
return err;
*max_access = max(access_end, *max_access);
+ if (write && size) {
+ if (!aux->max_rdwr_write || access_end - size < aux->min_rdwr_write)
+ aux->min_rdwr_write = access_end - size;
+ aux->max_rdwr_write = max(access_end, aux->max_rdwr_write);
+ }
return 0;
}
@@ -5643,6 +6131,7 @@ static int check_buffer_access(struct bpf_verifier_env *env,
/* BPF architecture zero extends alu32 ops into 64-bit registesr */
static void zext_32_to_64(struct bpf_reg_state *reg)
{
+ reg_step_check_unsigned(reg, 32);
reg->var_off = tnum_subreg(reg->var_off);
reg_set_urange64(reg, reg_u32_min(reg), reg_u32_max(reg));
}
@@ -5652,13 +6141,14 @@ static void zext_32_to_64(struct bpf_reg_state *reg)
*/
static void coerce_reg_to_size(struct bpf_reg_state *reg, int size)
{
- u64 mask;
+ u64 mask = (1ULL << (size * 8)) - 1;
+
+ reg_step_check_unsigned(reg, size * 8);
/* clear high bits in bit representation */
reg->var_off = tnum_cast(reg->var_off, size);
/* fix arithmetic bounds */
- mask = ((u64)1 << (size * 8)) - 1;
if ((reg_umin(reg) & ~mask) == (reg_umax(reg) & ~mask))
reg_set_urange64(reg, reg_umin(reg) & mask, reg_umax(reg) & mask);
else
@@ -5695,6 +6185,14 @@ static void coerce_reg_to_size_sx(struct bpf_reg_state *reg, int size)
s64 init_s64_max, init_s64_min, s64_max, s64_min, u64_cval;
u64 top_smax_value, top_smin_value;
u64 num_bits = size * 8;
+ s64 limit = 1LL << (num_bits - 1);
+
+ /* If every value fits the signed N-bit range sign extension is a noop. */
+ if (reg_smin(reg) >= -limit && reg_smax(reg) < limit)
+ return;
+
+ /* See commen at reg_step_check_unsigned(). */
+ reg_step_reset(reg);
if (tnum_is_const(reg->var_off)) {
u64_cval = reg->var_off.value;
@@ -5761,6 +6259,16 @@ static void coerce_subreg_to_size_sx(struct bpf_reg_state *reg, int size)
u32 top_smax_value, top_smin_value;
u32 num_bits = size * 8;
+ /*
+ * If value fits the non-negative half of the signed N-bit range,
+ * sign extension is a noop.
+ */
+ if (reg_umax(reg) < (1ULL << (num_bits - 1)))
+ return;
+
+ /* See comment at reg_step_check_unsigned(). */
+ reg_step_reset(reg);
+
if (tnum_is_const(reg->var_off)) {
u32_val = reg->var_off.value;
if (size == 1)
@@ -5854,6 +6362,110 @@ int bpf_map_direct_read(struct bpf_map *map, int off, int size, u64 *val,
return 0;
}
+static int cmp_func_ptrs(const void *_a, const void *_b)
+{
+ const struct bpf_func_ptr *a = _a, *b = _b;
+
+ if (a->map != b->map)
+ return a->map < b->map ? -1 : 1;
+ if (a->map_off != b->map_off)
+ return a->map_off < b->map_off ? -1 : 1;
+ return 0;
+}
+
+/* Find the first pointer to a function at or after 'off' in the value of 'map' */
+static u32 func_ptr_lower_bound(struct bpf_verifier_env *env, const struct bpf_map *map, u64 off)
+{
+ u32 l = 0, r = env->func_ptr_cnt, m;
+ struct bpf_func_ptr *p;
+
+ while (l < r) {
+ m = l + (r - l) / 2;
+ p = &env->func_ptrs[m];
+ if (p->map < map || (p->map == map && p->map_off < off))
+ l = m + 1;
+ else
+ r = m;
+ }
+ return l;
+}
+
+/*
+ * Return pointers to functions that overlap with 'size' bytes at offset 'off'
+ * of the value of 'map' and their number in 'cnt'.
+ */
+struct bpf_func_ptr *bpf_map_range_func_ptrs(struct bpf_verifier_env *env,
+ const struct bpf_map *map,
+ u64 off, u64 size, u32 *cnt)
+{
+ u32 first, last;
+
+ *cnt = 0;
+ if (!env->func_ptr_cnt || !size)
+ return NULL;
+
+ /* a pointer that starts up to 7 bytes before 'off' overlaps too */
+ first = func_ptr_lower_bound(env, map, off >= sizeof(u64) ? off - sizeof(u64) + 1 : 0);
+ last = func_ptr_lower_bound(env, map, off + size);
+ if (first >= last)
+ return NULL;
+
+ *cnt = last - first;
+ return &env->func_ptrs[first];
+}
+
+/* Return all pointers to functions in the value of 'map' */
+struct bpf_func_ptr *bpf_map_func_ptrs(struct bpf_verifier_env *env,
+ const struct bpf_map *map, u32 *cnt)
+{
+ return bpf_map_range_func_ptrs(env, map, 0, (u64)map->value_size, cnt);
+}
+
+/* instructions [off, off + len) replaced the instruction at 'off' */
+void bpf_adjust_func_ptrs(struct bpf_verifier_env *env, u32 off, u32 len)
+{
+ struct bpf_func_ptr *p;
+ u32 i;
+
+ if (len <= 1)
+ return;
+
+ for (i = 0; i < env->func_ptr_cnt; i++) {
+ p = &env->func_ptrs[i];
+ if (p->xlated_off <= off || p->xlated_off == BPF_FUNC_PTR_DELETED)
+ continue;
+ p->xlated_off += len - 1;
+ }
+}
+
+/*
+ * Instructions [off, off + len) are about to be removed. It's called before
+ * the starts of subprogs are adjusted. A subprog is gone when all of its
+ * instructions are. Otherwise, e.g. when its first instruction is a nop,
+ * it starts where the removed instructions did.
+ */
+void bpf_adjust_func_ptrs_after_remove(struct bpf_verifier_env *env, u32 off, u32 len)
+{
+ struct bpf_func_ptr *p;
+ int subprog;
+ u32 i;
+
+ for (i = 0; i < env->func_ptr_cnt; i++) {
+ p = &env->func_ptrs[i];
+ if (p->xlated_off < off || p->xlated_off == BPF_FUNC_PTR_DELETED)
+ continue;
+ if (p->xlated_off >= off + len) {
+ p->xlated_off -= len;
+ continue;
+ }
+ subprog = bpf_find_subprog(env, p->xlated_off);
+ if (subprog > 0 && env->subprog_info[subprog + 1].start > off + len)
+ p->xlated_off = off;
+ else
+ p->xlated_off = BPF_FUNC_PTR_DELETED;
+ }
+}
+
#define BTF_TYPE_SAFE_RCU(__type) __PASTE(__type, __safe_rcu)
#define BTF_TYPE_SAFE_RCU_OR_NULL(__type) __PASTE(__type, __safe_rcu_or_null)
#define BTF_TYPE_SAFE_TRUSTED(__type) __PASTE(__type, __safe_trusted)
@@ -5923,10 +6535,36 @@ BTF_TYPE_SAFE_TRUSTED(struct file) {
struct inode *f_inode;
};
+/*
+ * The pointer fields in the sched_ext ops argument containers are pinned by the
+ * callers for the duration of the ops calls and are never NULL.
+ */
+BTF_TYPE_SAFE_TRUSTED(struct scx_init_task_args) {
+#ifdef CONFIG_EXT_GROUP_SCHED
+ struct cgroup *cgroup;
+#endif
+};
+
+BTF_TYPE_SAFE_TRUSTED(struct scx_cpu_release_args) {
+ struct task_struct *task;
+};
+
+BTF_TYPE_SAFE_TRUSTED(struct scx_sub_attach_args) {
+ struct sched_ext_ops *ops;
+};
+
+BTF_TYPE_SAFE_TRUSTED(struct scx_sub_detach_args) {
+ struct sched_ext_ops *ops;
+};
+
BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct dentry) {
struct inode *d_inode;
};
+BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct linux_binprm) {
+ struct mm_struct *mm;
+};
+
BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) {
struct sock *sk;
};
@@ -5967,6 +6605,10 @@ static bool type_is_trusted(struct bpf_verifier_env *env,
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task));
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct linux_binprm));
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct file));
+ BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct scx_init_task_args));
+ BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct scx_cpu_release_args));
+ BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct scx_sub_attach_args));
+ BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct scx_sub_detach_args));
return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_trusted");
}
@@ -5977,12 +6619,52 @@ static bool type_is_trusted_or_null(struct bpf_verifier_env *env,
{
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket));
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct dentry));
+ BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct linux_binprm));
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct vm_area_struct));
return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id,
"__safe_trusted_or_null");
}
+/*
+ * The member holds the kernel address of arena memory, which the rewritten
+ * load turns into an arena pointer, see arena_member_fixup(). NULL needs no
+ * tracking, PTR_TO_ARENA covers it.
+ */
+static int check_arena_member_load(struct bpf_verifier_env *env, struct bpf_reg_state *regs,
+ int value_regno, enum bpf_type_flag flag, const char *tname,
+ const char *field_name)
+{
+ struct bpf_insn_aux_data *aux = &env->insn_aux_data[env->insn_idx];
+ struct bpf_insn *insn;
+
+ /* a helper reading the member as bytes needs no rewrite */
+ if (value_regno < 0)
+ return 0;
+ if (!env->prog->aux->arena) {
+ verbose(env,
+ "arena pointer member %s of struct %s requires a program with an associated arena\n",
+ field_name, tname);
+ return -EINVAL;
+ }
+ /* only a plain load or an acquire load is rewritten */
+ insn = &env->prog->insnsi[env->insn_idx];
+ if (BPF_CLASS(insn->code) != BPF_LDX &&
+ !(insn->code == (BPF_STX | BPF_ATOMIC | BPF_DW) && insn->imm == BPF_LOAD_ACQ)) {
+ verbose(env,
+ "arena pointer member %s of struct %s can only be read with a plain or an acquire load\n",
+ field_name, tname);
+ return -EACCES;
+ }
+
+ aux->arena_member = true;
+ if (flag & PTR_MAYBE_NULL)
+ aux->arena_member_nullable = true;
+ mark_reg_unknown(env, regs, value_regno);
+ regs[value_regno].type = PTR_TO_ARENA;
+ return 0;
+}
+
static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
struct bpf_reg_state *regs, struct bpf_reg_state *reg,
argno_t argno, int off, int size,
@@ -5994,6 +6676,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
const char *field_name = NULL;
enum bpf_type_flag flag = 0;
u32 btf_id = 0;
+ s64 min_off;
int ret;
if (!env->allow_ptr_leaks) {
@@ -6009,36 +6692,31 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
return -EINVAL;
}
- if (!tnum_is_const(reg->var_off)) {
- char tn_buf[48];
-
- tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
+ if (check_add_overflow(reg_smin(reg), off, &min_off)) {
verbose(env,
- "%s is ptr_%s invalid variable offset: off=%d, var_off=%s\n",
- reg_arg_name(env, argno), tname, off, tn_buf);
+ "%s is ptr_%s access, offset computation overflows: register's minimal offset is %lld, instruction offset is %d\n",
+ reg_arg_name(env, argno), tname, reg_smin(reg), off);
return -EACCES;
}
- off += reg->var_off.value;
-
- if (off < 0) {
+ if (min_off < 0) {
verbose(env,
- "%s is ptr_%s invalid negative access: off=%d\n",
- reg_arg_name(env, argno), tname, off);
+ "%s is ptr_%s invalid negative access: off=%lld\n",
+ reg_arg_name(env, argno), tname, min_off);
return -EACCES;
}
if (reg->type & MEM_USER) {
verbose(env,
- "%s is ptr_%s access user memory: off=%d\n",
- reg_arg_name(env, argno), tname, off);
+ "%s is ptr_%s access user memory\n",
+ reg_arg_name(env, argno), tname);
return -EACCES;
}
if (reg->type & MEM_PERCPU) {
verbose(env,
- "%s is ptr_%s access percpu memory: off=%d\n",
- reg_arg_name(env, argno), tname, off);
+ "%s is ptr_%s access percpu memory\n",
+ reg_arg_name(env, argno), tname);
return -EACCES;
}
@@ -6048,6 +6726,18 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
}
if (env->ops->btf_struct_access && !type_is_alloc(reg->type) && atype == BPF_WRITE) {
+ if (!tnum_is_const(reg->var_off)) {
+ char tn_buf[48];
+
+ tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
+ verbose(env,
+ "%s is ptr_%s invalid variable offset: off=%d, var_off=%s\n",
+ reg_arg_name(env, argno), tname, off, tn_buf);
+ return -EACCES;
+ }
+
+ off += reg->var_off.value;
+
if (!btf_is_kernel(reg->btf)) {
verifier_bug(env, "reg->btf must be kernel btf");
return -EFAULT;
@@ -6084,6 +6774,9 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
if (ret < 0)
return ret;
+ if (ret == PTR_TO_ARENA)
+ return check_arena_member_load(env, regs, value_regno, flag, tname, field_name);
+
if (ret != PTR_TO_BTF_ID) {
/* just mark; */
@@ -6144,6 +6837,16 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
clear_trusted_flags(&flag);
}
+ /*
+ * A __nullable member only matters where NULL would otherwise be
+ * assumed away, which is anywhere but on a plain or an untrusted
+ * pointer: those are probed on a dereference, which tolerates NULL, and
+ * the BTF_TYPE_SAFE_*_OR_NULL lists never applied to them either. A
+ * per-CPU pointer counts, per_cpu_ptr() does not check for NULL.
+ */
+ if (bpf_may_fault_on_deref(PTR_TO_BTF_ID | (flag & ~PTR_MAYBE_NULL)))
+ flag &= ~PTR_MAYBE_NULL;
+
if (atype == BPF_READ && value_regno >= 0) {
ret = mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
if (ret < 0)
@@ -6218,10 +6921,11 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
return 0;
}
-/* Check that the stack access at the given offset is within bounds. The
+/*
+ * Check that the stack access at the given offset is within bounds. The
* maximum valid offset is -1.
*
- * The minimum valid offset is -MAX_BPF_STACK for writes, and
+ * The minimum valid offset is -env->stack_limit for writes, and
* -state->allocated_stack for reads.
*/
static int check_stack_slot_within_bounds(struct bpf_verifier_env *env,
@@ -6232,7 +6936,7 @@ static int check_stack_slot_within_bounds(struct bpf_verifier_env *env,
int min_valid_off;
if (t == BPF_WRITE || env->allow_uninit_stack)
- min_valid_off = -MAX_BPF_STACK;
+ min_valid_off = -(int)env->stack_limit;
else
min_valid_off = -state->allocated_stack;
@@ -6298,10 +7002,19 @@ static int check_stack_access_within_bounds(
return err;
}
+ /*
+ * Every stack access passes through here, including buffers passed to
+ * helpers and kfuncs and accesses into a caller's frame, so check the
+ * fastcall contract of the frame that owns the slots once for all of
+ * them. Zero-sized accesses touch no stack and keep the rewrite enabled.
+ */
+ if (access_size)
+ check_fastcall_stack_contract(env, state, env->insn_idx, min_off);
+
/* Note that there is no stack access with offset zero, so the needed stack
* size is -min_off, not -min_off+1.
*/
- return grow_stack_state(env, state, -min_off /* size */);
+ return bpf_grow_stack_state(env, state, -min_off /* size */);
}
static bool get_func_retval_range(struct bpf_prog *prog,
@@ -6325,6 +7038,7 @@ static void add_scalar_to_reg(struct bpf_reg_state *dst_reg, s64 val)
fake_reg.type = SCALAR_VALUE;
__mark_reg_known(&fake_reg, val);
+ scalar_step_add(dst_reg, dst_reg, &fake_reg);
scalar32_min_max_add(dst_reg, &fake_reg);
scalar_min_max_add(dst_reg, &fake_reg);
dst_reg->var_off = tnum_add(dst_reg->var_off, fake_reg.var_off);
@@ -6332,12 +7046,147 @@ static void add_scalar_to_reg(struct bpf_reg_state *dst_reg, s64 val)
reg_bounds_sync(dst_reg);
}
+/* there is no support for callx in the interpreter */
+static int require_callx_jit(struct bpf_verifier_env *env)
+{
+ if (!env->prog->jit_requested) {
+ verbose(env, "JIT is required to use callx\n");
+ return -EOPNOTSUPP;
+ }
+ if (!bpf_jit_supports_callx()) {
+ verbose(env, "JIT doesn't support callx\n");
+ return -EOPNOTSUPP;
+ }
+ env->prog->jit_required = true;
+ return 0;
+}
+
+static void mark_reg_func_ptr(struct bpf_verifier_env *env, struct bpf_reg_state *regs,
+ int regno, int subprog)
+{
+ mark_reg_known_zero(env, regs, regno);
+ regs[regno].type = PTR_TO_FUNC;
+ regs[regno].subprogno = subprog;
+}
+
+/* a read from a table of functions branches into that many states at most */
+#define BPF_MAX_FUNC_PTR_TARGETS 64
+/* and that many pointers at most are next to what is read at variable offset */
+#define BPF_MAX_FUNC_PTR_RANGE 4096
+
+/* Count up to 'limit' offsets in [min_off, max_off] that the read is possible at */
+static u32 count_read_offsets(struct tnum offs, u64 min_off, u64 max_off, u32 limit)
+{
+ u64 o = min_off, next;
+ u32 cnt = 0;
+
+ if (!tnum_in(offs, tnum_const(o))) {
+ next = tnum_step(offs, o);
+ if (next <= o)
+ return 0;
+ o = next;
+ }
+ while (o <= max_off && cnt < limit) {
+ cnt++;
+ next = tnum_step(offs, o);
+ if (next <= o)
+ break;
+ o = next;
+ }
+ return cnt;
+}
+
+/*
+ * A read from a frozen read-only map that has pointers to functions, see
+ * resolve_func_ptrs(). The bounds and var_off of the offset tell where the read
+ * is possible at, e.g. var_off is the stride of the elements of an array.
+ * A read of exactly one pointer yields PTR_TO_FUNC. When the offset is variable
+ * and only pointers can be read, which is how an element of a table of
+ * functions is loaded, the verification continues with each of them. Other
+ * reads that overlap with a pointer are rejected, because their result is not
+ * known until the program is jitted.
+ *
+ * Return -ENOENT if there are no pointers to functions in the bytes that are read.
+ */
+static int check_func_ptr_read(struct bpf_verifier_env *env, struct bpf_reg_state *reg, int off,
+ int size, int value_regno)
+{
+ u64 min_off = reg_umin(reg) + off, max_off = reg_umax(reg) + off;
+ struct tnum offs = tnum_add(reg->var_off, tnum_const(off));
+ struct bpf_reg_state *regs = cur_regs(env);
+ struct bpf_map *map = reg->map_ptr;
+ struct bpf_verifier_state *branch;
+ int subprog, targets[BPF_MAX_FUNC_PTR_TARGETS];
+ struct bpf_func_ptr *ptrs;
+ u32 i, cnt, n = 0;
+ u64 o, lo, hi;
+ int err;
+
+ ptrs = bpf_map_range_func_ptrs(env, map, min_off, max_off - min_off + size, &cnt);
+ if (!ptrs)
+ return -ENOENT;
+ if (cnt > BPF_MAX_FUNC_PTR_RANGE)
+ goto overlap;
+
+ for (i = 0; i < cnt; i++) {
+ /* the offsets at which the read overlaps with the pointer */
+ lo = ptrs[i].map_off >= size ? ptrs[i].map_off - size + 1 : 0;
+ lo = max(lo, min_off);
+ hi = min(ptrs[i].map_off + sizeof(u64) - 1, max_off);
+ for (o = lo; o <= hi; o++) {
+ if (!tnum_in(offs, tnum_const(o)))
+ continue;
+ if (o != ptrs[i].map_off || size != sizeof(u64) || value_regno < 0)
+ goto overlap;
+ if (n == BPF_MAX_FUNC_PTR_TARGETS) {
+ verbose(env, "read from map '%s' may yield more than %d pointers to functions\n",
+ map->name, BPF_MAX_FUNC_PTR_TARGETS);
+ return -E2BIG;
+ }
+ subprog = bpf_find_subprog(env, ptrs[i].xlated_off);
+ if (verifier_bug_if(subprog <= 0, env,
+ "no function at insn %u for map '%s' offset %u",
+ ptrs[i].xlated_off, map->name, ptrs[i].map_off))
+ return -EFAULT;
+ targets[n++] = subprog;
+ }
+ }
+ /* pointers are next to what is read, e.g. the data of an array of vtables */
+ if (!n)
+ return -ENOENT;
+ /* either every offset that the read is possible at is a pointer or none */
+ if (count_read_offsets(offs, min_off, max_off, n + 1) != n)
+ goto overlap;
+
+ /* the map has the offset of the function until the program is jitted */
+ err = require_callx_jit(env);
+ if (err)
+ return err;
+
+ for (i = 0; i < n - 1; i++) {
+ branch = push_stack(env, env->insn_idx + 1, env->insn_idx,
+ env->cur_state->speculative);
+ if (IS_ERR(branch))
+ return PTR_ERR(branch);
+ mark_reg_func_ptr(env, branch->frame[branch->curframe]->regs, value_regno,
+ targets[i]);
+ }
+ mark_reg_func_ptr(env, regs, value_regno, targets[n - 1]);
+ return 0;
+
+overlap:
+ verbose(env, "read of %d bytes at offset [%llu,%llu] of map '%s' overlaps with a pointer to a function\n",
+ size, min_off, max_off, map->name);
+ return -EACCES;
+}
+
static int check_map_mem_read(struct bpf_verifier_env *env, struct bpf_reg_state *reg, int off,
int bpf_size, int value_regno, bool is_ldsx)
{
struct bpf_reg_state *regs = cur_regs(env);
int size = bpf_size_to_bytes(bpf_size);
struct bpf_map *map = reg->map_ptr;
+ int err;
switch (map->map_type) {
case BPF_MAP_TYPE_INSN_ARRAY:
@@ -6355,13 +7204,18 @@ static int check_map_mem_read(struct bpf_verifier_env *env, struct bpf_reg_state
break;
}
+ if (env->func_ptr_cnt) {
+ err = check_func_ptr_read(env, reg, off, size, value_regno);
+ if (err != -ENOENT)
+ return err;
+ }
+
/* If map is read-only, track its contents as scalars. */
if (tnum_is_const(reg->var_off) &&
bpf_map_is_rdonly(map) &&
map->ops->map_direct_value_addr) {
int map_off = off + reg->var_off.value;
u64 val = 0;
- int err;
err = bpf_map_direct_read(map, map_off, size, &val, is_ldsx);
if (err)
@@ -6388,6 +7242,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
int value_regno, bool strict_alignment_once, bool is_ldsx)
{
struct bpf_reg_state *regs = cur_regs(env);
+ enum bpf_reg_type ptr_type = reg->type;
int size, err = 0;
size = bpf_size_to_bytes(bpf_size);
@@ -6513,6 +7368,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
regs[value_regno].btf = info.btf;
regs[value_regno].btf_id = info.btf_id;
regs[value_regno].id = info.ref_id;
+ } else if (base_type(info.reg_type) == PTR_TO_MEM) {
+ regs[value_regno].mem_size = info.mem_size;
}
if (type_may_be_null(info.reg_type) && !regs[value_regno].id)
regs[value_regno].id = ++env->id_gen;
@@ -6594,7 +7451,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
}
err = check_buffer_access(env, reg, argno, off, size, false,
- max_access);
+ max_access, t == BPF_WRITE);
if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ))
mark_reg_unknown(env, regs, value_regno);
@@ -6635,12 +7492,32 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
clear_scalar_id(&regs[value_regno]);
}
}
+
+ if (!err && bpf_is_mem_insn(&env->prog->insnsi[insn_idx]) && ptr_type != PTR_TO_STACK)
+ mark_non_stack_access(env, insn_idx);
+
return err;
}
static int save_aux_ptr_type(struct bpf_verifier_env *env, enum bpf_reg_type type,
bool allow_trust_mismatch);
+/*
+ * Returns the register to check the access of the current insn with.
+ * For a number in a program with an arena that is 'arena'.
+ */
+static struct bpf_reg_state *mem_access_reg(struct bpf_verifier_env *env, int regno,
+ struct bpf_reg_state *arena)
+{
+ if (!is_arena_scalar(env, regno))
+ return cur_regs(env) + regno;
+
+ memset(arena, 0, sizeof(*arena));
+ arena->type = PTR_TO_ARENA;
+ env->insn_aux_data[env->insn_idx].arena_scalar = true;
+ return arena;
+}
+
static int check_load_mem(struct bpf_verifier_env *env, struct bpf_insn *insn,
bool strict_alignment_once, bool is_ldsx,
bool allow_trust_mismatch, const char *ctx)
@@ -6648,6 +7525,7 @@ static int check_load_mem(struct bpf_verifier_env *env, struct bpf_insn *insn,
struct bpf_verifier_state *vstate = env->cur_state;
struct bpf_func_state *state = vstate->frame[vstate->curframe];
struct bpf_reg_state *regs = cur_regs(env);
+ struct bpf_reg_state arena, *src_reg;
enum bpf_reg_type src_reg_type;
int err;
@@ -6669,16 +7547,28 @@ static int check_load_mem(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (err)
return err;
- src_reg_type = regs[insn->src_reg].type;
+ src_reg = mem_access_reg(env, insn->src_reg, &arena);
+ src_reg_type = src_reg->type;
/*
* check_stack_read_fixed_off() may refine the modification's origin to
* the source stack slot.
*/
bpf_diag_mod_begin(env, &regs[insn->dst_reg], NULL, BPF_DIAG_MOD_WRITE);
- err = check_mem_access(env, env->insn_idx, regs + insn->src_reg, argno_from_reg(insn->src_reg), insn->off,
- BPF_SIZE(insn->code), BPF_READ, insn->dst_reg,
+ err = check_mem_access(env, env->insn_idx, src_reg, argno_from_reg(insn->src_reg),
+ insn->off, BPF_SIZE(insn->code), BPF_READ, insn->dst_reg,
strict_alignment_once, is_ldsx);
+ /*
+ * Paths through a kernel struct pointer and through other pointers
+ * may share the insn, see save_aux_ptr_type(). Record a load that did
+ * not produce an arena pointer from an __arena member, the only load
+ * through a kernel struct pointer that does, next to the one
+ * check_arena_member_load() records, so that arena_member_fixup() can
+ * tell when the paths disagree.
+ */
+ if (!err && !(base_type(src_reg_type) == PTR_TO_BTF_ID &&
+ regs[insn->dst_reg].type == PTR_TO_ARENA))
+ env->insn_aux_data[env->insn_idx].non_arena_load = true;
err = err ?: save_aux_ptr_type(env, src_reg_type,
allow_trust_mismatch);
err = err ?: reg_bounds_sanity_check(env, &regs[insn->dst_reg], ctx);
@@ -6694,6 +7584,7 @@ static int check_store_reg(struct bpf_verifier_env *env, struct bpf_insn *insn,
struct bpf_verifier_state *vstate = env->cur_state;
struct bpf_func_state *state = vstate->frame[vstate->curframe];
struct bpf_reg_state *regs = cur_regs(env);
+ struct bpf_reg_state arena, *dst_reg;
enum bpf_reg_type dst_reg_type;
int err;
@@ -6715,11 +7606,12 @@ static int check_store_reg(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (err)
return err;
- dst_reg_type = regs[insn->dst_reg].type;
+ dst_reg = mem_access_reg(env, insn->dst_reg, &arena);
+ dst_reg_type = dst_reg->type;
/* Check if (dst_reg + off) is writeable. */
- err = check_mem_access(env, env->insn_idx, regs + insn->dst_reg, argno_from_reg(insn->dst_reg), insn->off,
- BPF_SIZE(insn->code), BPF_WRITE, insn->src_reg,
+ err = check_mem_access(env, env->insn_idx, dst_reg, argno_from_reg(insn->dst_reg),
+ insn->off, BPF_SIZE(insn->code), BPF_WRITE, insn->src_reg,
strict_alignment_once, false);
err = err ?: save_aux_ptr_type(env, dst_reg_type, false);
@@ -6729,7 +7621,7 @@ static int check_store_reg(struct bpf_verifier_env *env, struct bpf_insn *insn,
static int check_atomic_rmw(struct bpf_verifier_env *env,
struct bpf_insn *insn)
{
- struct bpf_reg_state *dst_reg;
+ struct bpf_reg_state arena, *dst_reg;
int load_reg;
int err;
@@ -6774,6 +7666,9 @@ static int check_atomic_rmw(struct bpf_verifier_env *env,
return -EACCES;
}
+ /* load_reg may be dst_reg. Look at dst_reg before it's marked as unknown. */
+ dst_reg = mem_access_reg(env, insn->dst_reg, &arena);
+
load_reg = bpf_atomic_load_reg(insn);
if (load_reg >= 0) {
/* check and record load of old value */
@@ -6782,8 +7677,6 @@ static int check_atomic_rmw(struct bpf_verifier_env *env,
return err;
}
- dst_reg = cur_regs(env) + insn->dst_reg;
-
/* Check whether we can read the memory, with second call for fetch
* case to simulate the register fill.
*/
@@ -6910,8 +7803,11 @@ 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);
+ u32 arg_slot = arg_slot_from_argno(argno);
+ bool uninit = clobber && meta && arg_slot < MAX_BPF_FUNC_ARGS &&
+ (meta->arg_raw_mem.mask & BIT(arg_slot));
+ bool raw_mode = uninit && env->allow_uninit_stack &&
+ !(meta->arg_raw_mem.var_size_mask & BIT(arg_slot));
access_size = abs(access_size);
@@ -6940,11 +7836,9 @@ static int check_stack_range_initialized(
reg_arg_name(env, argno), tn_buf);
return -EACCES;
}
- /* Only initialized buffer on stack is allowed to be accessed
- * with variable offset. With uninitialized buffer it's hard to
- * guarantee that whole memory is marked as initialized on
- * helper return since specific bounds are unknown what may
- * cause uninitialized stack leaking.
+ /*
+ * The call may touch any byte in the possible range, but does not
+ * definitely initialize all of it. Fall back to ordinary stack checks.
*/
raw_mode = false;
@@ -6952,12 +7846,14 @@ static int check_stack_range_initialized(
max_off = reg_smax(reg) + off;
}
+ /* Unprivileged outputs retain each byte's initialization state. */
if (raw_mode) {
- meta->arg_raw_mem.size = access_size;
+ meta->arg_raw_mem.size[arg_slot] = access_size;
return 0;
}
for (i = min_off; i < max_off + access_size; i++) {
+ struct bpf_stack_state *ss;
u8 *stype;
slot = -i - 1;
@@ -6967,32 +7863,41 @@ static int check_stack_range_initialized(
return -EFAULT;
}
- stype = &state->stack[spi].slot_type[slot % BPF_REG_SIZE];
+ ss = bpf_stack_slot(state, spi);
+ stype = &ss->slot_type[slot % BPF_REG_SIZE];
if (*stype == STACK_MISC)
goto mark;
if ((*stype == STACK_ZERO) ||
- (*stype == STACK_INVALID && env->allow_uninit_stack)) {
- if (clobber) {
+ (*stype == STACK_INVALID && (uninit || env->allow_uninit_stack))) {
+ if (clobber && (*stype != STACK_INVALID || env->allow_uninit_stack)) {
/* helper can write anything into the stack */
*stype = STACK_MISC;
}
goto mark;
}
- if (bpf_is_spilled_reg(&state->stack[spi]) &&
- (state->stack[spi].spilled_ptr.type == SCALAR_VALUE ||
+ /*
+ * Only the bytes marked STACK_SPILL hold the spilled register.
+ * The rest of a narrowly spilled slot keeps its previous type
+ * and must be initialized on its own.
+ */
+ if (*stype == STACK_SPILL &&
+ (ss->spilled_ptr.type == SCALAR_VALUE ||
env->allow_ptr_leaks)) {
if (clobber) {
- __mark_reg_unknown(env, &state->stack[spi].spilled_ptr);
+ __mark_reg_unknown(env, &ss->spilled_ptr);
for (j = 0; j < BPF_REG_SIZE; j++)
- scrub_spilled_slot(&state->stack[spi].slot_type[j]);
+ scrub_spilled_slot(&ss->slot_type[j]);
}
goto mark;
}
if (*stype == STACK_POISON) {
- if (allow_poison)
+ if (allow_poison) {
+ if (uninit && env->allow_uninit_stack)
+ *stype = STACK_MISC;
goto mark;
+ }
verbose(env, "reading from stack %s off %d+%d size %d, slot poisoned by dead code elimination\n",
reg_arg_name(env, argno), min_off, i - min_off, access_size);
} else if (tnum_is_const(reg->var_off)) {
@@ -7026,6 +7931,10 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_
switch (base_type(reg->type)) {
case PTR_TO_PACKET:
case PTR_TO_PACKET_META:
+ if (!may_access_direct_pkt_data(env, meta, access_type)) {
+ verbose(env, "function access to the packet is not allowed\n");
+ return -EACCES;
+ }
return check_packet_access(env, reg, argno, 0, access_size,
zero_size_allowed);
case PTR_TO_MAP_KEY:
@@ -7066,7 +7975,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_
}
return check_buffer_access(env, reg, argno, 0,
access_size, zero_size_allowed,
- max_access);
+ max_access, access_type == BPF_WRITE);
case PTR_TO_STACK:
return check_stack_range_initialized(
env, reg,
@@ -7136,13 +8045,13 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
*/
meta->msize_max_value = reg_umax(size_reg);
- /* The register is SCALAR_VALUE; the access check happens using
- * its boundaries. For unprivileged variable accesses, disable
- * raw mode so that the program is required to initialize all
- * the memory that the helper could just partially fill up.
+ /*
+ * Check variable ranges byte by byte instead of using raw mode. Keep the
+ * MEM_UNINIT annotation so invalid bytes are accepted without marking them
+ * initialized when the caller cannot read uninitialized stack memory.
*/
if (!tnum_is_const(size_reg->var_off))
- meta = NULL;
+ meta->arg_raw_mem.var_size_mask |= BIT(arg_slot_from_argno(mem_argno));
if (reg_smin(size_reg) < 0) {
verbose(env, "%s min value is negative, either use unsigned or 'var &= const'\n",
@@ -7185,6 +8094,11 @@ size_error:
return err;
}
+static bool is_subprog(const struct bpf_call_arg_meta *meta)
+{
+ return meta->btf && !meta->func_id;
+}
+
static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
argno_t argno, u32 mem_size, enum bpf_access_type access_type,
struct bpf_call_arg_meta *meta, bool *known_memory)
@@ -7203,10 +8117,11 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
}
/*
- * Only a global subprog (meta == NULL) may read poisoned stack slots:
+ * Only a global subprog may read poisoned stack slots:
* its static stack liveness proved the callee body skips them.
*/
- size = (!meta && base_type(reg->type) == PTR_TO_STACK) ? -(int)mem_size : mem_size;
+ size = (is_subprog(meta) &&
+ base_type(reg->type) == PTR_TO_STACK) ? -(int)mem_size : mem_size;
if (access_type & BPF_READ)
err = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta,
@@ -7498,6 +8413,9 @@ static int check_map_field_pointer(struct bpf_verifier_env *env, struct bpf_reg_
case BPF_TASK_WORK:
field_off = map->record->task_work_off;
break;
+ case BPF_RCU_HEAD:
+ field_off = map->record->rcu_head_off;
+ break;
case BPF_WORKQUEUE:
field_off = map->record->wq_off;
break;
@@ -7586,11 +8504,11 @@ __printf(6, 7) static void bpf_diag_call_arg_fmt(struct bpf_verifier_env *env, u
/*
* Validate dynptr arguments for helper, kfunc and subprog.
*
- * @dynptr is both input and output. It is populated when the argument is
- * tagged with MEM_UNINIT (i.e., the dynptr argument that will be constructed)
- * and consumed when the argument is expecting to be an initialized dynptr.
- * @parent_id is used to track the referenced parent object (e.g., file or skb in
- * qdisc program) when constructing a dynptr.
+ * @meta carries the dynptr and referenced-object state. The dynptr is populated
+ * when the argument is tagged with MEM_UNINIT (i.e., the dynptr argument that
+ * will be constructed) and consumed when the argument is expected to be an
+ * initialized dynptr. The reference tracks the parent object (e.g., file or skb
+ * in qdisc program) when constructing a dynptr.
*
* There are two register types representing a bpf_dynptr, one is PTR_TO_STACK
* which points to a stack slot, and the other is CONST_PTR_TO_DYNPTR.
@@ -7607,24 +8525,11 @@ __printf(6, 7) static void bpf_diag_call_arg_fmt(struct bpf_verifier_env *env, u
* and checked dynamically during runtime.
*/
static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
- argno_t argno, int insn_idx, const char *call_name,
- enum bpf_arg_type arg_type,
- struct ref_obj_desc *ref_obj, struct bpf_dynptr_desc *dynptr)
+ argno_t argno, int insn_idx, enum bpf_arg_type arg_type,
+ struct bpf_call_arg_meta *meta)
{
int spi, err = 0;
- if (reg->type != PTR_TO_STACK && reg->type != CONST_PTR_TO_DYNPTR) {
- verbose(env,
- "%s expected pointer to stack or const struct bpf_dynptr\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, call_name,
- "Pass the address of a stack dynptr object, or use a const dynptr pointer returned by the verifier-supported path.",
- "a dynptr argument must be a pointer to a dynptr stack slot or a verifier-provided const struct bpf_dynptr, but %s is %s",
- reg_arg_name(env, argno), bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
/* MEM_UNINIT - Points to memory that is an appropriate candidate for
* constructing a mutable bpf_dynptr object.
*
@@ -7646,7 +8551,7 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
verbose(env, "Dynptr has to be an uninitialized dynptr\n");
bpf_diag_res(
env, insn_idx, "dynptr is already initialized",
- "This kfunc constructs a dynptr and requires an uninitialized dynptr stack slot, but the selected slot already holds dynptr state.",
+ "This function constructs a dynptr and requires an uninitialized dynptr stack slot, but the selected slot already holds dynptr state.",
"Use a fresh stack dynptr slot, or release/destroy the existing dynptr before reusing the slot.");
return -EINVAL;
}
@@ -7659,7 +8564,8 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
return err;
}
- err = mark_stack_slots_dynptr(env, reg, arg_type, insn_idx, ref_obj, dynptr);
+ err = mark_stack_slots_dynptr(env, reg, arg_type, insn_idx,
+ &meta->ref_obj, &meta->dynptr);
} else /* OBJ_RELEASE and None case from above */ {
/* For the reg->type == PTR_TO_STACK case, bpf_dynptr is never const */
if (reg->type == CONST_PTR_TO_DYNPTR && (arg_type & OBJ_RELEASE)) {
@@ -7689,7 +8595,7 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
verbose(env, "Expected a dynptr of type %s as %s\n",
dynptr_type_str(expected_type), reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(
- env, insn_idx, argno, call_name,
+ env, insn_idx, argno, meta->func_name,
"Use a dynptr constructor that matches this operation, or call an operation that accepts the dynptr's current type.",
"the dynptr is initialized with backing object type %s, but this operation expects dynptr type %s",
dynptr_type_str(actual_type), dynptr_type_str(expected_type));
@@ -7705,14 +8611,12 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
mark_stack_slots_scratched(env, spi, BPF_DYNPTR_NR_SLOTS);
- reg = &state->stack[spi].spilled_ptr;
+ reg = &bpf_stack_slot(state, spi)->spilled_ptr;
}
- if (dynptr) {
- dynptr->type = reg->dynptr.type;
- dynptr->id = reg->id;
- dynptr->parent_id = reg->parent_id;
- }
+ meta->dynptr.type = reg->dynptr.type;
+ meta->dynptr.id = reg->id;
+ meta->dynptr.parent_id = reg->parent_id;
}
return err;
}
@@ -7745,12 +8649,12 @@ static bool is_kfunc_arg_iter(struct bpf_call_arg_meta *meta, int arg_idx,
return btf_param_match_suffix(meta->btf, arg, "__iter");
}
-static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno, int insn_idx,
+static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ u32 arg, argno_t argno, int insn_idx,
struct bpf_call_arg_meta *meta)
{
struct bpf_func_state *state = bpf_func(env, reg);
const struct btf_type *t;
- u32 arg_idx = arg_idx_from_argno(argno);
int spi, err, i, nr_slots, btf_id;
if (reg->type != PTR_TO_STACK) {
@@ -7770,14 +8674,14 @@ static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *
* to any kfunc, if arg has "__iter" suffix, we need to be a bit more
* conservative here.
*/
- btf_id = btf_check_iter_arg(meta->btf, meta->func_proto, arg_idx);
+ btf_id = btf_check_iter_arg(meta->btf, meta->func_proto, arg);
if (btf_id < 0) {
verbose(env, "expected valid iter pointer as %s\n",
reg_arg_name(env, argno));
bpf_diag_call_arg(
env, insn_idx, argno, meta->func_name,
- "the kfunc expects a recognized iterator state pointer, but this argument does not match a valid iterator type",
- "Pass the exact iterator state type expected by this kfunc.");
+ "the function expects a recognized iterator state pointer, but this argument does not match a valid iterator type",
+ "Pass the exact iterator state type expected by this function.");
return -EINVAL;
}
t = btf_type_by_id(meta->btf, btf_id);
@@ -7840,7 +8744,7 @@ static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *
/* remember meta->iter info for process_iter_next_call() */
meta->iter.spi = spi;
meta->iter.frameno = reg->frameno;
- update_ref_obj(&meta->ref_obj, &state->stack[spi].spilled_ptr);
+ update_ref_obj(&meta->ref_obj, &bpf_stack_slot(state, spi)->spilled_ptr);
if (is_iter_destroy_kfunc(meta)) {
err = unmark_stack_slots_iter(env, reg, nr_slots);
@@ -7917,16 +8821,15 @@ static int widen_imprecise_scalars(struct bpf_verifier_env *env,
&fold->regs[i],
&fcur->regs[i]);
- num_slots = min(fold->allocated_stack / BPF_REG_SIZE,
- fcur->allocated_stack / BPF_REG_SIZE);
+ num_slots = min(bpf_stack_nr_slots(fold), bpf_stack_nr_slots(fcur));
for (i = 0; i < num_slots; i++) {
- if (!bpf_is_spilled_reg(&fold->stack[i]) ||
- !bpf_is_spilled_reg(&fcur->stack[i]))
+ struct bpf_stack_state *old_ss = bpf_stack_slot(fold, i);
+ struct bpf_stack_state *cur_ss = bpf_stack_slot(fcur, i);
+
+ if (!bpf_is_spilled_reg(old_ss) || !bpf_is_spilled_reg(cur_ss))
continue;
- maybe_widen_reg(env,
- &fold->stack[i].spilled_ptr,
- &fcur->stack[i].spilled_ptr);
+ maybe_widen_reg(env, &old_ss->spilled_ptr, &cur_ss->spilled_ptr);
}
}
return 0;
@@ -7938,7 +8841,7 @@ static struct bpf_reg_state *get_iter_from_state(struct bpf_verifier_state *cur_
int iter_frameno = meta->iter.frameno;
int iter_spi = meta->iter.spi;
- return &cur_st->frame[iter_frameno]->stack[iter_spi].spilled_ptr;
+ return &bpf_stack_slot(cur_st->frame[iter_frameno], iter_spi)->spilled_ptr;
}
/* process_iter_next_call() is called when verifier gets to iterator's next
@@ -8101,9 +9004,52 @@ static bool arg_type_is_dynptr(enum bpf_arg_type type)
return base_type(type) == ARG_PTR_TO_DYNPTR;
}
+/*
+ * An argument that only ever takes a scalar, so a zero register passed to it
+ * is a value rather than a NULL pointer.
+ */
+static bool arg_type_is_scalar(enum bpf_arg_type type)
+{
+ switch (base_type(type)) {
+ case ARG_SCALAR:
+ case ARG_CONST_SCALAR:
+ case ARG_MEM_SIZE:
+ case ARG_MEM_SIZE_OR_ZERO:
+ case ARG_CONST_MEM_SIZE:
+ case ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * A helper has no BTF and a nonzero function ID. A kfunc has both, while a
+ * BPF subprogram has BTF and a zero function ID.
+ */
+static bool is_helper(const struct bpf_call_arg_meta *meta)
+{
+ return !meta->btf && meta->func_id;
+}
+
+static bool is_helper_call(const struct bpf_call_arg_meta *meta, enum bpf_func_id func_id)
+{
+ return is_helper(meta) && meta->func_id == func_id;
+}
+
+static bool is_kfunc(const struct bpf_call_arg_meta *meta)
+{
+ return meta->btf && meta->func_id;
+}
+
+static bool is_kfunc_call(const struct bpf_call_arg_meta *meta, u32 btf_id)
+{
+ return is_kfunc(meta) && meta->func_id == btf_id;
+}
+
static int resolve_map_arg_type(struct bpf_verifier_env *env,
- const struct bpf_call_arg_meta *meta,
- enum bpf_arg_type *arg_type)
+ const struct bpf_call_arg_meta *meta,
+ enum bpf_arg_type *arg_type)
{
if (!meta->map.ptr) {
/* kernel subsystem misconfigured verifier */
@@ -8122,7 +9068,7 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
}
break;
case BPF_MAP_TYPE_BLOOM_FILTER:
- if (meta->func_id == BPF_FUNC_map_peek_elem)
+ if (is_helper_call(meta, BPF_FUNC_map_peek_elem))
*arg_type = ARG_PTR_TO_MAP_VALUE;
break;
default:
@@ -8131,6 +9077,49 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
return 0;
}
+static int resolve_func_arg_type(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, u32 arg, argno_t argno,
+ struct bpf_call_arg_meta *meta,
+ enum bpf_arg_type *arg_type, u32 *arg_size);
+static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, enum bpf_arg_type arg_type,
+ const struct btf *arg_btf, u32 arg_btf_id,
+ struct bpf_call_arg_meta *meta, int insn_idx);
+static bool is_kfunc_arg_nonown_allowed(const struct btf *btf,
+ const struct btf_param *arg);
+static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,
+ const struct btf_param *arg,
+ const char *name);
+static bool is_bpf_cast_to_kern_ctx_kfunc(const struct bpf_call_arg_meta *meta);
+static bool is_bpf_dynptr_clone_kfunc(const struct bpf_call_arg_meta *meta);
+static bool is_bpf_iter_css_task_new_kfunc(const struct bpf_call_arg_meta *meta);
+static bool is_kfunc_dynptr_write(struct bpf_call_arg_meta *meta);
+static bool is_bpf_obj_drop_kfunc(u32 func_id);
+static bool is_bpf_percpu_obj_drop_kfunc(u32 func_id);
+static bool is_bpf_rbtree_add_kfunc(u32 func_id);
+static int get_bpf_res_spin_lock_kfunc_flags(const struct bpf_call_arg_meta *meta);
+static struct bpf_insn_aux_data *cur_aux(const 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_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_list_head(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_rbtree_root(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_list_node(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_rbtree_node(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static bool check_css_task_iter_allowlist(struct bpf_verifier_env *env);
+
struct bpf_reg_types {
const enum bpf_reg_type types[10];
u32 *btf_id;
@@ -8174,7 +9163,7 @@ static const struct bpf_reg_types mem_types = {
},
};
-static const struct bpf_reg_types spin_lock_types = {
+static const struct bpf_reg_types map_value_or_alloc_obj_types = {
.types = {
PTR_TO_MAP_VALUE,
PTR_TO_BTF_ID | MEM_ALLOC,
@@ -8203,7 +9192,33 @@ static const struct bpf_reg_types percpu_btf_ptr_types = {
static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
-static const struct bpf_reg_types timer_types = { .types = { PTR_TO_MAP_VALUE } };
+static const struct bpf_reg_types map_value_types = { .types = { PTR_TO_MAP_VALUE } };
+static const struct bpf_reg_types arena_types = {
+ .types = {
+ PTR_TO_ARENA,
+ SCALAR_VALUE,
+ }
+};
+static const struct bpf_reg_types ctx_out_types = {
+ .types = { PTR_TO_MEM | MEM_RDONLY | PTR_TRUSTED },
+};
+
+static const struct bpf_reg_types alloc_obj_drop_types = {
+ .types = {
+ PTR_TO_BTF_ID | MEM_ALLOC,
+ PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU,
+ }
+};
+
+static const struct bpf_reg_types alloc_obj_types = {
+ .types = {
+ PTR_TO_BTF_ID | MEM_ALLOC,
+ PTR_TO_BTF_ID | MEM_ALLOC | MEM_RCU,
+ PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
+ PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU,
+ }
+};
+
static const struct bpf_reg_types kptr_xchg_dest_types = {
.types = {
PTR_TO_MAP_VALUE,
@@ -8234,16 +9249,32 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
#endif
[ARG_PTR_TO_SOCKET] = &fullsock_types,
[ARG_PTR_TO_BTF_ID] = &btf_ptr_types,
- [ARG_PTR_TO_SPIN_LOCK] = &spin_lock_types,
+ [ARG_PTR_TO_SPIN_LOCK] = &map_value_or_alloc_obj_types,
[ARG_PTR_TO_MEM] = &mem_types,
[ARG_PTR_TO_RINGBUF_MEM] = &ringbuf_mem_types,
[ARG_PTR_TO_PERCPU_BTF_ID] = &percpu_btf_ptr_types,
[ARG_PTR_TO_FUNC] = &func_ptr_types,
[ARG_PTR_TO_STACK] = &stack_ptr_types,
[ARG_PTR_TO_CONST_STR] = &const_str_ptr_types,
- [ARG_PTR_TO_TIMER] = &timer_types,
+ [ARG_PTR_TO_TIMER] = &map_value_types,
[ARG_KPTR_XCHG_DEST] = &kptr_xchg_dest_types,
[ARG_PTR_TO_DYNPTR] = &dynptr_types,
+ [ARG_CONST_SCALAR] = &scalar_types,
+ [ARG_CONST_MEM_SIZE] = &scalar_types,
+ [ARG_PTR_TO_ALLOC_BTF_ID] = &alloc_obj_drop_types,
+ [ARG_PTR_TO_REFCOUNTED_KPTR] = &alloc_obj_types,
+ [ARG_PTR_TO_ITER] = &stack_ptr_types,
+ [ARG_PTR_TO_LIST_HEAD] = &map_value_or_alloc_obj_types,
+ [ARG_PTR_TO_LIST_NODE] = &alloc_obj_types,
+ [ARG_PTR_TO_RB_ROOT] = &map_value_or_alloc_obj_types,
+ [ARG_PTR_TO_RB_NODE] = &alloc_obj_types,
+ [ARG_PTR_TO_RES_SPIN_LOCK] = &map_value_or_alloc_obj_types,
+ [ARG_PTR_TO_WORKQUEUE] = &map_value_types,
+ [ARG_PTR_TO_TASK_WORK] = &map_value_types,
+ [ARG_PTR_TO_RCU_HEAD] = &map_value_types,
+ [ARG_PTR_TO_IRQ_FLAG] = &stack_ptr_types,
+ [ARG_PTR_TO_ARENA] = &arena_types,
+ [ARG_PTR_TO_CTX_OUT] = &ctx_out_types,
};
static void bpf_diag_call_arg(struct bpf_verifier_env *env, u32 insn_idx, argno_t argno,
@@ -8283,6 +9314,71 @@ __printf(6, 7) static void bpf_diag_call_arg_fmt(struct bpf_verifier_env *env, u
bpf_diag_call_arg(env, insn_idx, argno, call_name, reason, suggestion);
}
+static int check_func_arg_nullability(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, u32 arg, argno_t argno,
+ enum bpf_arg_type arg_type,
+ struct bpf_call_arg_meta *meta, int insn_idx)
+{
+ const char *expected_type = "pointer";
+
+ if (arg_type_is_scalar(arg_type) || type_may_be_null(arg_type) ||
+ (!bpf_register_is_null(reg) && !type_may_be_null(reg->type)))
+ return 0;
+
+ if (meta->btf) {
+ u32 arg_btf_id;
+
+ arg_btf_id = btf_params(meta->func_proto)[arg].type;
+ expected_type = bpf_diag_fmt(env, "value of type %s",
+ bpf_diag_fmt_btf_type(env, meta->btf, arg_btf_id));
+ }
+
+ verbose(env, "Possibly NULL pointer passed to trusted %s\n",
+ reg_arg_name(env, argno));
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Add a NULL check and make the call only on the non-NULL path.",
+ "the pointer may be NULL, but this call requires a non-NULL %s",
+ expected_type);
+ return -EACCES;
+}
+
+static int check_func_arg_release(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ u32 arg, argno_t argno, enum bpf_arg_type arg_type,
+ struct bpf_call_arg_meta *meta, int insn_idx)
+{
+ const char *expected_type = "pointer";
+
+ if (!arg_type_is_release(arg_type))
+ return 0;
+
+ if (arg_type_is_dynptr(arg_type) || reg_is_referenced(env, reg) ||
+ bpf_register_is_null(reg))
+ return 0;
+
+ verbose(env, "release function %s expects referenced PTR_TO_BTF_ID passed to %s\n",
+ meta->func_name, reg_arg_name(env, argno));
+
+ if (is_kfunc(meta)) {
+ const struct btf_param *btf_arg;
+ const struct btf_type *t;
+ u32 ref_id;
+
+ btf_arg = &btf_params(meta->func_proto)[arg];
+ ref_id = btf_arg->type;
+ t = btf_type_skip_modifiers(meta->btf, btf_arg->type, NULL);
+ if (btf_type_is_ptr(t))
+ btf_type_skip_modifiers(meta->btf, t->type, &ref_id);
+ expected_type = bpf_diag_fmt(env, "value of type %s",
+ bpf_diag_fmt_btf_type(env, meta->btf, ref_id));
+ }
+
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ bpf_diag_fmt(env, "Pass the resource-owning %s returned by the matching acquire call, or avoid the release function after ownership has already been transferred or released.",
+ expected_type),
+ "release functions require a value that owns a live resource returned by a matching acquire function");
+ return -EINVAL;
+}
+
static const char *bpf_diag_expected_reg_types(struct bpf_verifier_env *env,
const enum bpf_reg_type *types, int count)
{
@@ -8304,19 +9400,21 @@ static const char *bpf_diag_expected_reg_types(struct bpf_verifier_env *env,
}
static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
- enum bpf_arg_type arg_type, const u32 *arg_btf_id,
- struct bpf_call_arg_meta *meta, const char *call_name)
+ enum bpf_arg_type arg_type, struct bpf_call_arg_meta *meta)
{
enum bpf_reg_type expected, type = reg->type;
const struct bpf_reg_types *compatible;
const char *actual, *accepted;
- int i, j, err;
+ int i, j;
compatible = compatible_reg_types[base_type(arg_type)];
if (!compatible) {
verifier_bug(env, "unsupported arg type %d", arg_type);
return -EFAULT;
}
+ if (is_kfunc(meta) && base_type(arg_type) == ARG_PTR_TO_BTF_ID &&
+ (base_type(type) == PTR_TO_BTF_ID || reg2btf_ids[base_type(type)]))
+ goto found;
/* ARG_PTR_TO_MEM + RDONLY is compatible with PTR_TO_MEM and PTR_TO_MEM + RDONLY,
* but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM and NOT with PTR_TO_MEM + RDONLY
@@ -8336,9 +9434,14 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
type &= ~PTR_MAYBE_NULL;
if (base_type(arg_type) == ARG_PTR_TO_MEM)
type &= ~DYNPTR_TYPE_FLAG_MASK;
+ /* Allow allocated memory for BTF-defined ARG_PTR_TO_MEM but not helpers. */
+ if (!is_helper(meta) && base_type(arg_type) == ARG_PTR_TO_MEM &&
+ type_is_ptr_alloc_obj(type))
+ type = PTR_TO_MEM;
/* Local kptr types are allowed as the source argument of bpf_kptr_xchg */
- if (meta->func_id == BPF_FUNC_kptr_xchg && type_is_alloc(type) && reg_from_argno(argno) == BPF_REG_2) {
+ if (is_helper_call(meta, BPF_FUNC_kptr_xchg) && type_is_alloc(type) &&
+ reg_from_argno(argno) == BPF_REG_2) {
type &= ~MEM_ALLOC;
type &= ~MEM_PERCPU;
}
@@ -8358,115 +9461,13 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
verbose(env, "%s\n", reg_type_str(env, compatible->types[j]));
actual = bpf_diag_fmt(env, "%s", reg_type_str(env, reg->type));
accepted = bpf_diag_expected_reg_types(env, compatible->types, i);
- bpf_diag_call_arg_fmt(env, env->insn_idx, argno, call_name,
- "Pass a value with one of the accepted pointer or scalar types for this call.",
+ bpf_diag_call_arg_fmt(env, env->insn_idx, argno, meta->func_name,
+ bpf_diag_fmt(env, "Pass %s.", bpf_diag_arg_type_plain(arg_type)),
"it has type %s, but this argument accepts %s",
actual, accepted);
return -EACCES;
found:
- if (base_type(reg->type) != PTR_TO_BTF_ID)
- return 0;
-
- if (compatible == &mem_types) {
- if (!(arg_type & MEM_RDONLY)) {
- verbose(env,
- "%s() may write into memory pointed by %s type=%s\n",
- func_id_name(meta->func_id),
- reg_arg_name(env, argno), reg_type_str(env, reg->type));
- return -EACCES;
- }
- return 0;
- }
-
- switch ((int)reg->type) {
- case PTR_TO_BTF_ID:
- case PTR_TO_BTF_ID | PTR_TRUSTED:
- case PTR_TO_BTF_ID | PTR_TRUSTED | PTR_MAYBE_NULL:
- case PTR_TO_BTF_ID | MEM_RCU:
- case PTR_TO_BTF_ID | PTR_MAYBE_NULL:
- case PTR_TO_BTF_ID | PTR_MAYBE_NULL | MEM_RCU:
- {
- /* For bpf_sk_release, it needs to match against first member
- * 'struct sock_common', hence make an exception for it. This
- * allows bpf_sk_release to work for multiple socket types.
- */
- bool strict_type_match = arg_type_is_release(arg_type) &&
- meta->func_id != BPF_FUNC_sk_release;
-
- if (type_may_be_null(reg->type) &&
- (!type_may_be_null(arg_type) || arg_type_is_release(arg_type))) {
- verbose(env, "Possibly NULL pointer passed to helper %s\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg(
- env, env->insn_idx, argno, call_name,
- "the pointer may be NULL, but this call requires a non-NULL pointer",
- "Add a NULL check and make the call only on the non-NULL path.");
- return -EACCES;
- }
-
- if (!arg_btf_id) {
- if (!compatible->btf_id) {
- verifier_bug(env, "missing arg compatible BTF ID");
- return -EFAULT;
- }
- arg_btf_id = compatible->btf_id;
- }
-
- if (meta->func_id == BPF_FUNC_kptr_xchg) {
- if (map_kptr_match_type(env, meta->kptr_field, reg, reg_from_argno(argno)))
- return -EACCES;
- } else {
- if (arg_btf_id == BPF_PTR_POISON) {
- verbose(env, "verifier internal error:");
- verbose(env, "%s has non-overwritten BPF_PTR_POISON type\n",
- reg_arg_name(env, argno));
- return -EACCES;
- }
-
- err = __check_ptr_off_reg(env, reg, argno, true);
- if (err)
- return err;
-
- if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id,
- reg->var_off.value, btf_vmlinux, *arg_btf_id,
- strict_type_match, !type_is_alloc(reg->type))) {
- verbose(env, "%s is of type %s but %s is expected\n",
- reg_arg_name(env, argno),
- btf_type_name(reg->btf, reg->btf_id),
- btf_type_name(btf_vmlinux, *arg_btf_id));
- return -EACCES;
- }
- }
- break;
- }
- case PTR_TO_BTF_ID | MEM_ALLOC:
- case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
- case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
- case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
- if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock &&
- meta->func_id != BPF_FUNC_kptr_xchg) {
- verifier_bug(env, "unimplemented handling of MEM_ALLOC");
- return -EFAULT;
- }
- /* Check if local kptr in src arg matches kptr in dst arg */
- if (meta->func_id == BPF_FUNC_kptr_xchg) {
- int regno = reg_from_argno(argno);
-
- if (regno == BPF_REG_2 &&
- map_kptr_match_type(env, meta->kptr_field, reg, regno))
- return -EACCES;
- }
- break;
- case PTR_TO_BTF_ID | MEM_PERCPU:
- case PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU:
- case PTR_TO_BTF_ID | MEM_PERCPU | PTR_TRUSTED:
- /* Handled by helper specific checks */
- break;
- default:
- verifier_bug(env, "invalid PTR_TO_BTF_ID register for type match");
- return -EFAULT;
- }
return 0;
}
@@ -8487,10 +9488,9 @@ reg_find_field_offset(const struct bpf_reg_state *reg, s32 off, u32 fields)
return field;
}
-static int __check_func_arg_reg_off(struct bpf_verifier_env *env,
- const struct bpf_reg_state *reg, argno_t argno,
- enum bpf_arg_type arg_type,
- bool btf_id_fixed_off_ok)
+static int check_func_arg_reg_off(struct bpf_verifier_env *env,
+ const struct bpf_reg_state *reg, argno_t argno,
+ enum bpf_arg_type arg_type)
{
u32 type = reg->type;
@@ -8546,12 +9546,15 @@ static int __check_func_arg_reg_off(struct bpf_verifier_env *env,
case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
/* When referenced PTR_TO_BTF_ID is passed to release function,
- * its fixed offset must be 0. In the other cases, fixed offset
- * can be non-zero unless the caller requires otherwise.
- * var_off always must be 0 for PTR_TO_BTF_ID, hence we still
- * need to do checks instead of returning.
+ * its fixed offset must be 0. bpf_refcount_acquire() returns the
+ * pointer it was given while incrementing the refcount at the
+ * refcount field offset, so it needs a zero offset too. In the
+ * other cases, fixed offset can be non-zero. var_off always must
+ * be 0 for PTR_TO_BTF_ID, hence we still need to do checks
+ * instead of returning.
*/
- return __check_ptr_off_reg(env, reg, argno, btf_id_fixed_off_ok);
+ return __check_ptr_off_reg(env, reg, argno,
+ base_type(arg_type) != ARG_PTR_TO_REFCOUNTED_KPTR);
case PTR_TO_CTX:
/*
* Allow fixed and variable offsets for syscall context, but
@@ -8559,7 +9562,7 @@ static int __check_func_arg_reg_off(struct bpf_verifier_env *env,
* otherwise we may get modified ctx in tail called programs and
* global subprogs (that may act as extension prog hooks).
*/
- if (arg_type != ARG_PTR_TO_CTX && is_var_ctx_off_allowed(env->prog))
+ if (base_type(arg_type) != ARG_PTR_TO_CTX && is_var_ctx_off_allowed(env->prog))
return 0;
fallthrough;
default:
@@ -8567,13 +9570,6 @@ static int __check_func_arg_reg_off(struct bpf_verifier_env *env,
}
}
-static int check_func_arg_reg_off(struct bpf_verifier_env *env,
- const struct bpf_reg_state *reg, argno_t argno,
- enum bpf_arg_type arg_type)
-{
- return __check_func_arg_reg_off(env, reg, argno, arg_type, true);
-}
-
static int check_arg_const_str(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, argno_t argno)
{
@@ -8582,6 +9578,7 @@ static int check_arg_const_str(struct bpf_verifier_env *env,
int map_off;
u64 map_addr;
char *str_ptr;
+ u32 cnt;
if (reg->type != PTR_TO_MAP_VALUE)
return -EINVAL;
@@ -8631,6 +9628,11 @@ static int check_arg_const_str(struct bpf_verifier_env *env,
verbose(env, "string is not zero-terminated\n");
return -EINVAL;
}
+ /* the bytes of a pointer to a function are not known until the program is jitted */
+ if (bpf_map_range_func_ptrs(env, map, map_off, strlen(str_ptr + map_off) + 1, &cnt)) {
+ verbose(env, "string overlaps with a pointer to a function\n");
+ return -EACCES;
+ }
return 0;
}
@@ -8641,6 +9643,7 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
s64 *value)
{
struct bpf_func_state *state = bpf_func(env, key);
+ struct bpf_stack_state *ss;
struct bpf_reg_state *reg;
int slot, spi, off;
int spill_size = 0;
@@ -8660,7 +9663,8 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
slot = -stack_off - 1;
spi = slot / BPF_REG_SIZE;
off = slot % BPF_REG_SIZE;
- stype = state->stack[spi].slot_type;
+ ss = bpf_stack_slot(state, spi);
+ stype = ss->slot_type;
/* First handle precisely tracked STACK_ZERO */
for (i = off; i >= 0 && stype[i] == STACK_ZERO; i--)
@@ -8671,14 +9675,14 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
}
/* Check that stack contains a scalar spill of expected size */
- if (!bpf_is_spilled_scalar_reg(&state->stack[spi]))
+ if (!bpf_is_spilled_scalar_reg(ss))
return -EOPNOTSUPP;
for (i = off; i >= 0 && stype[i] == STACK_SPILL; i--)
spill_size++;
if (spill_size != key_size)
return -EOPNOTSUPP;
- reg = &state->stack[spi].spilled_ptr;
+ reg = &ss->spilled_ptr;
if (!tnum_is_const(reg->var_off))
/* Stack value not statically known */
return -EOPNOTSUPP;
@@ -8722,6 +9726,8 @@ static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_stat
obj_name = "timer";
else if (rec->task_work_off >= 0)
obj_name = "bpf_task_work";
+ else if (rec->rcu_head_off >= 0)
+ obj_name = "bpf_rcu_head";
verbose(env, "%s pointer in %s map_uid=%d ",
obj_name, reg_arg_name(env, obj_argno), meta->map.uid);
@@ -8735,65 +9741,89 @@ static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_stat
return 0;
}
-static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
+/*
+ * MEM_WRITE alone denotes an input/output buffer. MEM_UNINIT marks an output
+ * whose incoming contents are not read.
+ */
+static enum bpf_access_type func_arg_access_type(enum bpf_arg_type arg_type)
+{
+ if (!(arg_type & MEM_WRITE))
+ return BPF_READ;
+ if (arg_type & MEM_UNINIT)
+ return BPF_WRITE;
+ return BPF_READ | BPF_WRITE;
+}
+
+static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 prev_slot,
struct bpf_call_arg_meta *meta,
int insn_idx)
{
+ const struct btf_param *btf_arg = is_kfunc(meta) ?
+ &btf_params(meta->func_proto)[arg] : NULL;
const struct bpf_func_proto *fn = meta->fn;
- u32 regno = BPF_REG_1 + arg;
- struct bpf_reg_state *reg = reg_state(env, regno);
+ struct bpf_func_state *caller = cur_func(env);
+ struct bpf_reg_state *regs = cur_regs(env);
+ argno_t argno = argno_from_arg(slot + 1);
+ struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, slot);
enum bpf_arg_type arg_type = fn->arg_type[arg];
- argno_t argno = argno_from_reg(regno);
- enum bpf_reg_type type = reg->type;
- u32 *arg_btf_id = NULL;
+ int regno = reg_from_argno(argno);
+ u32 arg_size = arg_type & MEM_FIXED_SIZE ? fn->arg_size[arg] : 0;
u32 key_size;
int err = 0;
- if (arg_type == ARG_DONTCARE)
+ if (arg_type == ARG_PTR_TO_PROG_AUX) {
+ cur_aux(env)->arg_prog = regno;
return 0;
+ }
- err = check_reg_arg(env, regno, SRC_OP);
- if (err)
- return err;
+ if (arg_type == ARG_IGNORE)
+ return 0;
+
+ if (regno >= 0) {
+ err = check_reg_arg(env, regno, SRC_OP);
+ if (err)
+ return err;
+ }
+ /* Preserve the legacy helper behavior for privileged pointer leaks. */
if (arg_type == ARG_ANYTHING) {
- if (is_pointer_value(env, regno)) {
- verbose(env, "R%d leaks addr into helper function\n",
- regno);
+ if (__is_pointer_value(env->allow_ptr_leaks, reg)) {
+ verbose(env, "%s leaks addr into helper function\n",
+ reg_arg_name(env, argno));
return -EACCES;
}
return 0;
}
- if (type_is_pkt_pointer(type) &&
- !may_access_direct_pkt_data(env, fn, BPF_READ)) {
- verbose(env, "helper access to the packet is not allowed\n");
+ err = resolve_func_arg_type(env, reg, arg, argno, meta, &arg_type, &arg_size);
+ if (err)
+ return err;
+
+ /*
+ * A slice of a packet is packet data under another name. A helper which
+ * may reallocate the packet must not go on reading from the old one.
+ */
+ if (is_helper(meta) && reg_is_dynptr_slice_pkt(reg) &&
+ bpf_helper_changes_pkt_data(meta->func_id)) {
+ verbose(env, "function access to the packet is not allowed\n");
return -EACCES;
}
- if (base_type(arg_type) == ARG_PTR_TO_MAP_VALUE) {
- err = resolve_map_arg_type(env, meta, &arg_type);
- if (err)
- return err;
- }
+ if (arg_type_is_raw_mem(arg_type))
+ meta->arg_raw_mem.mask |= BIT(slot);
if (bpf_register_is_null(reg) && type_may_be_null(arg_type)) {
- /* A NULL register has a SCALAR_VALUE type, so skip
- * type checking.
- */
- err = mark_chain_precision(env, regno);
+ err = mark_arg_precision(env, argno);
if (err)
return err;
- goto skip_type_check;
+ return 0;
}
- /* arg_btf_id and arg_size are in a union. */
- if (base_type(arg_type) == ARG_PTR_TO_BTF_ID ||
- base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK)
- arg_btf_id = fn->arg_btf_id[arg];
+ err = check_func_arg_nullability(env, reg, arg, argno, arg_type, meta, insn_idx);
+ if (err)
+ return err;
- err = check_reg_type(env, reg, argno, arg_type, arg_btf_id, meta,
- func_id_name(meta->func_id));
+ err = check_reg_type(env, reg, argno, arg_type, meta);
if (err)
return err;
@@ -8801,22 +9831,27 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
if (err)
return err;
-skip_type_check:
- if (arg_type_is_release(arg_type) && !arg_type_is_dynptr(arg_type) &&
- !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
- verbose(env, "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
- func_id_name(meta->func_id), reg_arg_name(env, argno));
- bpf_diag_call_arg(
- env, insn_idx, argno, func_id_name(meta->func_id),
- "release helpers require a value that owns a live resource returned by a matching acquire helper",
- "Pass the resource-owning pointer returned by the matching acquire helper, and avoid calling the release helper after ownership has already been transferred or released.");
- return -EINVAL;
- }
+ err = check_func_arg_release(env, reg, arg, argno, arg_type, meta, insn_idx);
+ if (err)
+ return err;
if (reg_is_referenced(env, reg))
update_ref_obj(&meta->ref_obj, reg);
switch (base_type(arg_type)) {
+ case ARG_CONST_SCALAR:
+ err = process_const_arg(env, reg, argno, meta);
+ if (err < 0) {
+ if (err == -EINVAL)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
+ "the function requires this scalar argument to be a verifier-known constant, but %s is variable on this path",
+ reg_arg_name(env, argno));
+ return err;
+ }
+ break;
+ case ARG_SCALAR:
+ break;
case ARG_CONST_MAP_PTR:
/* bpf_map_xxx(map_ptr) call: remember that map_ptr */
err = process_map_ptr_arg(env, reg, argno, meta);
@@ -8838,7 +9873,7 @@ skip_type_check:
return -EFAULT;
}
key_size = meta->map.ptr->key_size;
- err = check_helper_mem_access(env, reg, argno, key_size, BPF_READ, false, NULL,
+ err = check_helper_mem_access(env, reg, argno, key_size, BPF_READ, false, meta,
NULL);
if (err)
return err;
@@ -8866,21 +9901,86 @@ skip_type_check:
return -EFAULT;
}
- /*
- * Disable raw mode for bpf_map_peek_elem() on a bloom filter. The helper reads
- * the value buffer as an input rather than filling it.
- */
- if (meta->func_id == BPF_FUNC_map_peek_elem &&
- meta->map.ptr->map_type == BPF_MAP_TYPE_BLOOM_FILTER)
- meta->arg_raw_mem.regno = 0;
-
err = check_helper_mem_access(env, reg, argno, meta->map.ptr->value_size,
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
false, meta, NULL);
break;
+ case ARG_PTR_TO_BTF_ID:
+ case ARG_PTR_TO_BTF_ID_SOCK_COMMON:
+ {
+ const u32 *arg_btf_id = fn->arg_btf_id[arg];
+ const struct btf *arg_btf = is_kfunc(meta) ? meta->btf : btf_vmlinux;
+ enum bpf_reg_type reg_type;
+
+ if (!meta->btf) {
+ const struct bpf_reg_types *compatible;
+
+ if (base_type(reg->type) != PTR_TO_BTF_ID)
+ break;
+
+ if (is_helper_call(meta, BPF_FUNC_kptr_xchg))
+ return map_kptr_match_type(env, meta->kptr_field, reg, regno) ?
+ -EACCES : 0;
+
+ if (!arg_btf_id) {
+ compatible = compatible_reg_types[base_type(arg_type)];
+ if (!compatible->btf_id) {
+ verifier_bug(env, "missing arg compatible BTF ID");
+ return -EFAULT;
+ }
+ arg_btf_id = compatible->btf_id;
+ }
+ if (arg_btf_id == BPF_PTR_POISON) {
+ verbose(env, "verifier internal error:");
+ verbose(env, "%s has non-overwritten BPF_PTR_POISON type\n",
+ reg_arg_name(env, argno));
+ return -EACCES;
+ }
+ }
+
+ reg_type = reg->type & ~(arg_type & PTR_MAYBE_NULL);
+ if ((arg_type & PTR_TRUSTED) &&
+ (!is_trusted_reg_type(env, reg, reg_type) ||
+ bpf_type_has_unsafe_modifiers(reg_type))) {
+ if (!(arg_type & MEM_RCU)) {
+ const char *actual_type, *arg_name, *expected_type;
+
+ expected_type = bpf_diag_fmt_btf_type(env, arg_btf, *arg_btf_id);
+ verbose(env, "%s must be referenced or trusted\n",
+ reg_arg_name(env, argno));
+ arg_name = reg_arg_name(env, argno);
+ actual_type = bpf_diag_reg_type_plain(env, reg->type);
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a pointer acquired from a verifier-tracked source, or call this function only inside the required protection if it accepts RCU pointers.",
+ "the function requires a trusted or resource-owning pointer to %s, but %s is %s",
+ expected_type, arg_name, actual_type);
+ return -EINVAL;
+ }
+ if (!is_rcu_reg(reg)) {
+ const char *actual_type, *arg_name, *expected_type;
+
+ expected_type = bpf_diag_fmt_btf_type(env, arg_btf, *arg_btf_id);
+ verbose(env, "%s must be a rcu pointer\n",
+ reg_arg_name(env, argno));
+ arg_name = reg_arg_name(env, argno);
+ actual_type = bpf_diag_reg_type_plain(env, reg->type);
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Use this function with a pointer that is valid in an RCU read lock region.",
+ "the function requires an RCU-protected pointer to %s, but %s is %s",
+ expected_type, arg_name, actual_type);
+ return -EINVAL;
+ }
+ }
+
+ err = process_arg_ptr_to_btf_id(env, reg, argno, arg_type, arg_btf,
+ *arg_btf_id, meta, insn_idx);
+ if (err < 0)
+ return err;
+ break;
+ }
case ARG_PTR_TO_PERCPU_BTF_ID:
if (!reg->btf_id) {
- verbose(env, "Helper has invalid btf_id in R%d\n", regno);
+ verbose(env, "Helper has invalid btf_id in %s\n", reg_arg_name(env, argno));
return -EACCES;
}
meta->ret_btf = reg->btf;
@@ -8891,11 +9991,11 @@ skip_type_check:
verbose(env, "can't spin_{lock,unlock} in rbtree cb\n");
return -EACCES;
}
- if (meta->func_id == BPF_FUNC_spin_lock) {
+ if (is_helper_call(meta, BPF_FUNC_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) {
+ } else if (is_helper_call(meta, BPF_FUNC_spin_unlock)) {
err = process_spin_lock(env, reg, argno, 0);
if (err)
return err;
@@ -8909,45 +10009,283 @@ skip_type_check:
if (err)
return err;
break;
+ case ARG_PTR_TO_CTX:
+ if (is_bpf_cast_to_kern_ctx_kfunc(meta)) {
+ err = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
+ if (err < 0)
+ return -EINVAL;
+ meta->ret_btf_id = err;
+ }
+ break;
+ case ARG_PTR_TO_ARENA:
+ break;
+ case ARG_PTR_TO_ALLOC_BTF_ID:
+ if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
+ if (!is_bpf_obj_drop_kfunc(meta->func_id)) {
+ verbose(env, "%s expected for bpf_obj_drop()\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ } else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU)) {
+ if (!is_bpf_percpu_obj_drop_kfunc(meta->func_id)) {
+ verbose(env, "%s expected for bpf_percpu_obj_drop()\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ }
+ if (!reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass the owned object pointer before it is released or transferred.",
+ "the allocated object pointer in %s must still carry verifier-tracked ownership, but this pointer no longer owns a live resource",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ if (meta->btf == btf_vmlinux) {
+ meta->arg_btf = reg->btf;
+ meta->arg_btf_id = reg->btf_id;
+ }
+ break;
case ARG_PTR_TO_FUNC:
meta->subprogno = reg->subprogno;
break;
case ARG_PTR_TO_MEM:
+ {
+ enum bpf_access_type access_type;
+ bool known_memory;
+
/* The access to this pointer is only checked when we hit the
* next is_mem_size argument below.
*/
- if (arg_type & MEM_FIXED_SIZE) {
- err = check_mem_reg(env, reg, argno_from_reg(regno), fn->arg_size[arg],
- arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta, NULL);
- if (err)
- return err;
- if (arg_type & MEM_ALIGNED)
- err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
+ if (!(arg_type & MEM_FIXED_SIZE))
+ break;
+
+ access_type = func_arg_access_type(arg_type);
+
+ err = check_mem_reg(env, reg, argno, arg_size, access_type, meta, &known_memory);
+ if (err < 0) {
+ if (known_memory)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass memory with at least the required number of accessible bytes and suitable read or write access.",
+ "the function expects %u bytes of memory, but the verifier cannot prove that %s provides a range of that size with the required read or write access",
+ arg_size,
+ bpf_diag_reg_type_plain(env, reg->type));
+ else
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass stack, map, context, or other verifier-known memory of the expected type and size, not an integer cast to a pointer.",
+ "the function expects %u bytes of memory, but it is %s and not verifier-known memory",
+ arg_size,
+ bpf_diag_reg_type_plain(env, reg->type));
+ return err;
}
+ /*
+ * PTR_TO_PACKET gets passed as PTR_TO_MEM, preventing us from adjusting
+ * bounds tracking information.
+ */
+ if (is_subprog(meta) && meta->subprog_may_change_pkt &&
+ (reg_is_pkt_pointer_any(reg) || reg_is_dynptr_slice_pkt(reg))) {
+ verbose(env,
+ "cannot pass packet pointer %s to %s(): function may change packet data\n",
+ reg_arg_name(env, argno), meta->func_name);
+ return -EINVAL;
+ }
+ if (arg_type & MEM_ALIGNED)
+ err = check_ptr_alignment(env, reg, 0, arg_size, true);
break;
+ }
+ case ARG_CONST_MEM_SIZE:
+ err = process_const_arg(env, reg, argno, meta);
+ if (err < 0) {
+ if (err == -EINVAL)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
+ "the function requires this memory size to be a verifier-known constant, but %s is variable on this path",
+ reg_arg_name(env, argno));
+ return err;
+ }
+ fallthrough;
case ARG_MEM_SIZE:
- err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
- argno_from_reg(regno - 1), argno,
- fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
- false, meta, NULL);
- break;
case ARG_MEM_SIZE_OR_ZERO:
- err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
- argno_from_reg(regno - 1), argno,
- fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
- true, meta, NULL);
+ {
+ struct bpf_reg_state *buff_reg = get_func_arg_reg(caller, regs, prev_slot);
+ argno_t buff_argno = argno_from_arg(prev_slot + 1);
+ enum bpf_mem_size_failure failure;
+ const char *buff_arg, *size_arg;
+ bool zero_size_allowed;
+ u32 access_type;
+
+ if (meta->btf && bpf_register_is_null(buff_reg))
+ break;
+
+ access_type = func_arg_access_type(fn->arg_type[arg - 1]);
+
+ zero_size_allowed = meta->btf || base_type(arg_type) == ARG_MEM_SIZE_OR_ZERO;
+
+ err = check_mem_size_reg(env, buff_reg, reg, buff_argno, argno,
+ access_type, zero_size_allowed, meta, &failure);
+ if (!err)
+ break;
+
+ buff_arg = bpf_diag_arg_name(env, buff_argno);
+ size_arg = bpf_diag_arg_name(env, argno);
+ verbose(env, "%s and ", reg_arg_name(env, buff_argno));
+ verbose(env, "%s memory, len pair leads to invalid memory access\n",
+ reg_arg_name(env, argno));
+ if (failure == BPF_MEM_SIZE_FAIL_MEMORY) {
+ bpf_diag_call_arg_fmt(env, insn_idx, buff_argno, meta->func_name,
+ "Pass a stack, map, context, or other verifier-known memory pointer, and keep the paired length within that object.",
+ "it is the memory pointer in a memory/length pair with %s, but %s does not provide a verifier-accessible range of the requested length",
+ size_arg, buff_arg);
+ } else if (failure == BPF_MEM_SIZE_FAIL_SIZE) {
+ if (reg_smin(reg) < 0)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
+ "the memory size in %s may be negative because its signed minimum is %lld",
+ size_arg, reg_smin(reg));
+ else if (!zero_size_allowed && reg_umin(reg) == 0)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Ensure the memory size is non-zero before this call.",
+ "the memory size in %s may be zero, but the function requires a non-zero size",
+ size_arg);
+ else
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
+ "the memory size in %s may reach %llu bytes, but variable memory accesses must stay below %u bytes",
+ size_arg, reg_umax(reg), BPF_MAX_VAR_SIZ);
+ }
break;
- case ARG_PTR_TO_DYNPTR:
- err = process_dynptr_func(env, reg, argno, insn_idx, func_id_name(meta->func_id),
- arg_type, &meta->ref_obj, &meta->dynptr);
+ }
+ case ARG_PTR_TO_DYNPTR: {
+ if (is_bpf_dynptr_clone_kfunc(meta) &&
+ (arg_type & MEM_UNINIT)) {
+ enum bpf_dynptr_type parent_type = meta->dynptr.type;
+
+ if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
+ verifier_bug(env, "no dynptr type for parent of clone");
+ return -EFAULT;
+ }
+
+ arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
+ }
+
+ err = process_dynptr_func(env, reg, argno, insn_idx, arg_type, meta);
if (err)
return err;
+ if (!arg && is_kfunc_dynptr_write(meta))
+ meta->dynptr_write_type = meta->dynptr.type;
+ break;
+ }
+ case ARG_PTR_TO_ITER:
+ if (is_bpf_iter_css_task_new_kfunc(meta) &&
+ !check_css_task_iter_allowlist(env)) {
+ verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
+ return -EINVAL;
+ }
+ err = process_iter_arg(env, reg, arg, argno, insn_idx, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_LIST_HEAD:
+ if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
+ !reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ err = process_kf_arg_ptr_to_list_head(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_RB_ROOT:
+ if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
+ !reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ err = process_kf_arg_ptr_to_rbtree_root(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_LIST_NODE:
+ if (!(is_kfunc_arg_nonown_allowed(meta->btf, btf_arg) &&
+ type_is_non_owning_ref(reg->type) && !reg_is_referenced(env, reg))) {
+ if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
+ verbose(env, "%s expected pointer to allocated object\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ if (!reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ }
+ err = process_kf_arg_ptr_to_list_node(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_RB_NODE:
+ if (is_bpf_rbtree_add_kfunc(meta->func_id)) {
+ if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
+ verbose(env, "%s expected pointer to allocated object\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ if (!reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!type_is_non_owning_ref(reg->type) &&
+ !reg_is_referenced(env, reg)) {
+ verbose(env, "%s can only take non-owning or refcounted bpf_rb_node pointer\n",
+ meta->func_name);
+ return -EINVAL;
+ }
+ if (in_rbtree_lock_required_cb(env)) {
+ verbose(env, "%s not allowed in rbtree cb\n", meta->func_name);
+ return -EINVAL;
+ }
+ }
+ err = process_kf_arg_ptr_to_rbtree_node(env, reg, argno, meta);
+ if (err < 0)
+ return err;
break;
case ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ if (meta->btf && is_kfunc_arg_scalar_with_name(meta->btf, btf_arg,
+ "rdonly_buf_size"))
+ meta->r0_rdonly = true;
err = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
- if (err)
+ if (err < 0) {
+ if (meta->btf && err == -EINVAL)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a verifier-known constant size for this function's buffer argument.",
+ "the function uses this argument as a return-buffer size, but %s is invalid or variable on this path",
+ reg_arg_name(env, argno));
return err;
+ }
+ break;
+ case ARG_PTR_TO_REFCOUNTED_KPTR:
+ {
+ struct btf_record *rec;
+
+ if (!type_is_non_owning_ref(reg->type) && reg_is_referenced(env, reg))
+ meta->arg_owning_ref = true;
+
+ rec = reg_btf_record(reg);
+ if (!rec) {
+ verifier_bug(env, "Couldn't find btf_record");
+ return -EFAULT;
+ }
+
+ if (rec->refcount_off < 0) {
+ verbose(env, "%s doesn't point to a type with bpf_refcount field\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ meta->arg_btf = reg->btf;
+ meta->arg_btf_id = reg->btf_id;
break;
+ }
case ARG_PTR_TO_CONST_STR:
{
err = check_arg_const_str(env, reg, argno);
@@ -8955,6 +10293,50 @@ skip_type_check:
return err;
break;
}
+ case ARG_PTR_TO_WORKQUEUE:
+ err = check_map_field_pointer(env, reg, argno, BPF_WORKQUEUE, &meta->map);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_TASK_WORK:
+ err = check_map_field_pointer(env, reg, argno, BPF_TASK_WORK, &meta->map);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_RCU_HEAD:
+ err = check_map_field_pointer(env, reg, argno, BPF_RCU_HEAD, &meta->map);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_IRQ_FLAG:
+ err = process_irq_flag(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_CTX_OUT:
+ if (reg->mem_size != arg_size) {
+ verbose(env, "%s expected %u bytes of ctx-provided memory, got %u\n",
+ reg_arg_name(env, argno), arg_size, reg->mem_size);
+ return -EINVAL;
+ }
+ break;
+ case ARG_PTR_TO_RES_SPIN_LOCK:
+ {
+ int flags;
+
+ if (in_rbtree_lock_required_cb(env)) {
+ verbose(env, "can't res_spin_{lock,unlock} in rbtree cb\n");
+ return -EACCES;
+ }
+
+ flags = get_bpf_res_spin_lock_kfunc_flags(meta);
+ if (!flags)
+ return -EFAULT;
+ err = process_spin_lock(env, reg, argno, flags);
+ if (err < 0)
+ return err;
+ break;
+ }
case ARG_KPTR_XCHG_DEST:
err = process_kptr_func(env, regno, meta);
if (err)
@@ -8965,6 +10347,139 @@ skip_type_check:
return err;
}
+/*
+ * The slots a parameter takes, from its BTF type. This has to agree with
+ * btf_func_model_arg_slots(), which answers the same from the size the
+ * func model recorded, or the verifier would check an argument at a slot
+ * the JIT does not place it at.
+ */
+static u32 btf_arg_slots(const struct btf_type *t)
+{
+ if (btf_type_is_int(t) || btf_type_is_struct(t))
+ return (t->size + BPF_REG_SIZE - 1) / BPF_REG_SIZE;
+ return 1;
+}
+
+static u32 btf_proto_slots(const struct btf *btf, const struct btf_type *func_proto)
+{
+ const struct btf_param *args = btf_params(func_proto);
+ u32 i, nargs = btf_type_vlen(func_proto), slots_used = 0;
+
+ for (i = 0; i < nargs; i++)
+ slots_used += btf_arg_slots(btf_type_skip_modifiers(btf, args[i].type, NULL));
+
+ return slots_used;
+}
+
+/* The argument slot @slot holds an eightbyte of a by-value argument. */
+static int check_arg_extra_slot(struct bpf_verifier_env *env, struct bpf_func_state *caller,
+ u32 slot, struct bpf_call_arg_meta *meta)
+{
+ struct bpf_reg_state *reg = get_func_arg_reg(caller, cur_regs(env), slot);
+ argno_t argno = argno_from_arg(slot + 1);
+ int regno = reg_from_argno(argno);
+ int err;
+
+ if (regno >= 0) {
+ err = check_reg_arg(env, regno, SRC_OP);
+ if (err)
+ return err;
+ }
+
+ return check_reg_type(env, reg, argno, ARG_SCALAR, meta);
+}
+
+static int check_func_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
+ int insn_idx)
+{
+ u32 slot = 0, prev_slot = 0, proto_slots, nslots;
+ struct bpf_func_state *caller = cur_func(env);
+ const struct btf_param *args = NULL;
+ u32 arg, nargs = MAX_BPF_FUNC_REG_ARGS;
+ int err;
+
+ if (meta->btf) {
+ args = btf_params(meta->func_proto);
+ nargs = btf_type_vlen(meta->func_proto);
+ }
+
+ /*
+ * A by-value argument of more than one eightbyte takes a slot per
+ * eightbyte, so the slots a call occupies are no longer its parameter
+ * count. Only a proto whose parameters take a slot each can name the
+ * argument a stack slot belongs to.
+ */
+ proto_slots = meta->btf ? btf_proto_slots(meta->btf, meta->func_proto) : nargs;
+
+ if (proto_slots > MAX_BPF_FUNC_REG_ARGS) {
+ err = check_outgoing_stack_args(env, caller, proto_slots, meta->func_name,
+ meta->btf, proto_slots == nargs ? args : NULL);
+ if (err)
+ return err;
+ }
+
+ for (arg = 0; arg < nargs; arg++, prev_slot = slot, slot += nslots) {
+ const struct btf_type *t;
+ u32 k;
+
+ nslots = 1;
+ if (args) {
+ t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
+ nslots = btf_arg_slots(t);
+ }
+
+ if (meta->fn->arg_type[arg] == ARG_UNUSED)
+ break;
+ err = check_func_arg(env, arg, slot, prev_slot, meta, insn_idx);
+ if (err)
+ return err;
+
+ /*
+ * check_func_arg() took the first slot. A parameter of more
+ * than one eightbyte is always a scalar, so every slot it
+ * takes beyond the first holds one too.
+ */
+ for (k = 1; k < nslots; k++) {
+ err = check_arg_extra_slot(env, caller, slot + k, meta);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int mark_raw_stack(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
+ int insn_idx)
+{
+ struct bpf_func_state *caller = cur_func(env);
+ u32 slot;
+ int i, err;
+
+ /*
+ * Validate every argument before initializing outputs: an input argument
+ * may alias an output buffer. Use the normal stack-write checks to discard
+ * stale spills and preserve the rules for special stack objects.
+ */
+ for (slot = 0; slot < MAX_BPF_FUNC_ARGS; slot++) {
+ struct bpf_reg_state *reg;
+ argno_t argno = argno_from_arg(slot + 1);
+
+ if (!meta->arg_raw_mem.size[slot])
+ continue;
+ reg = get_func_arg_reg(caller, cur_regs(env), slot);
+
+ for (i = 0; i < meta->arg_raw_mem.size[slot]; i++) {
+ err = check_mem_access(env, insn_idx, reg, argno, i, BPF_B,
+ BPF_WRITE, -1, false, false);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static bool may_update_sockmap(struct bpf_verifier_env *env, int func_id)
{
enum bpf_attach_type eatype = env->prog->expected_attach_type;
@@ -9004,12 +10519,71 @@ bool bpf_allow_tail_call_in_subprogs(struct bpf_verifier_env *env)
bpf_jit_supports_subprog_tailcalls();
}
+/*
+ * Is rcu_read_lock_held() true at run time? Unlike for in_rcu_cs(), disabled
+ * preemption or interrupts will not do.
+ */
+static bool has_rcu_read_lock(struct bpf_verifier_env *env)
+{
+ struct bpf_verifier_state *state = env->cur_state;
+
+ if (state->active_rcu_locks || in_rcu_callback(env))
+ return true;
+ if (in_sleepable(env))
+ return false;
+ return !env->no_rcu_read_lock || state->frame[0]->in_async_callback_fn;
+}
+
+/* May the map be used outside of an RCU read side critical section? */
+static bool map_needs_no_rcu(const struct bpf_map *map)
+{
+ switch (map->map_type) {
+ case BPF_MAP_TYPE_HASH:
+ case BPF_MAP_TYPE_RHASH:
+ case BPF_MAP_TYPE_LRU_HASH:
+ case BPF_MAP_TYPE_ARRAY:
+ case BPF_MAP_TYPE_PERCPU_HASH:
+ case BPF_MAP_TYPE_PERCPU_ARRAY:
+ case BPF_MAP_TYPE_LRU_PERCPU_HASH:
+ case BPF_MAP_TYPE_LPM_TRIE:
+ case BPF_MAP_TYPE_ARRAY_OF_MAPS:
+ case BPF_MAP_TYPE_HASH_OF_MAPS:
+ case BPF_MAP_TYPE_RINGBUF:
+ case BPF_MAP_TYPE_USER_RINGBUF:
+ case BPF_MAP_TYPE_INODE_STORAGE:
+ case BPF_MAP_TYPE_SK_STORAGE:
+ case BPF_MAP_TYPE_TASK_STORAGE:
+ case BPF_MAP_TYPE_CGRP_STORAGE:
+ case BPF_MAP_TYPE_QUEUE:
+ case BPF_MAP_TYPE_STACK:
+ case BPF_MAP_TYPE_ARENA:
+ case BPF_MAP_TYPE_INSN_ARRAY:
+ case BPF_MAP_TYPE_PROG_ARRAY:
+ case BPF_MAP_TYPE_BLOOM_FILTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int check_map_func_compatibility(struct bpf_verifier_env *env,
struct bpf_map *map, int func_id)
{
if (!map)
return 0;
+ /*
+ * check_map_prog_compatibility() asks the program. That misses the
+ * sleepable callback of another program, and an inner map. They may
+ * use any map under the RCU read lock. Disabled preemption or
+ * interrupts will not do: the maps ask for rcu_read_lock_held().
+ */
+ if (!has_rcu_read_lock(env) && !map_needs_no_rcu(map)) {
+ verbose(env, "map '%s' needs bpf_rcu_read_lock() in sleepable context\n",
+ map->name);
+ return -EINVAL;
+ }
+
/* We need a two way check, first is from map perspective ... */
switch (map->map_type) {
case BPF_MAP_TYPE_PROG_ARRAY:
@@ -9257,23 +10831,6 @@ error:
return -EINVAL;
}
-static bool check_raw_mode_ok(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
- if (fn->arg_type[i] == ARG_DONTCARE)
- break;
- 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;
- }
-
- return true;
-}
-
static bool check_args_pair_invalid(const struct bpf_func_proto *fn, int arg)
{
bool is_fixed = fn->arg_type[arg] & MEM_FIXED_SIZE;
@@ -9312,7 +10869,7 @@ static bool check_btf_id_ok(const struct bpf_func_proto *fn)
int i;
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
- if (fn->arg_type[i] == ARG_DONTCARE)
+ if (fn->arg_type[i] == ARG_UNUSED)
break;
if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID)
return !!fn->arg_btf_id[i];
@@ -9335,7 +10892,7 @@ static bool check_mem_arg_rw_flag_ok(const struct bpf_func_proto *fn)
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
enum bpf_arg_type arg_type = fn->arg_type[i];
- if (arg_type == ARG_DONTCARE)
+ if (arg_type == ARG_UNUSED)
break;
if (base_type(arg_type) != ARG_PTR_TO_MEM)
continue;
@@ -9353,7 +10910,7 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
enum bpf_arg_type arg_type = fn->arg_type[i];
- if (arg_type == ARG_DONTCARE)
+ if (arg_type == ARG_UNUSED)
break;
if (arg_type_is_release(arg_type)) {
if (meta->release_regno)
@@ -9365,9 +10922,41 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
return true;
}
-static int check_func_proto(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
+static bool check_arg_prog_aux(struct bpf_verifier_env *env,
+ const struct bpf_func_proto *proto)
{
- return check_raw_mode_ok(fn, meta) &&
+ bool seen = false;
+ argno_t argno;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(proto->arg_type); i++) {
+ if (proto->arg_type[i] == ARG_UNUSED)
+ break;
+ if (proto->arg_type[i] != ARG_PTR_TO_PROG_AUX)
+ continue;
+
+ if (seen) {
+ verifier_bug(env, "Only 1 prog->aux argument supported");
+ return false;
+ }
+
+ argno = argno_from_arg(i + 1);
+ if (reg_from_argno(argno) < 0) {
+ verbose(env, "%s prog->aux cannot be a stack argument\n",
+ reg_arg_name(env, argno));
+ return false;
+ }
+
+ seen = true;
+ }
+
+ return true;
+}
+
+static int check_func_proto(struct bpf_verifier_env *env, const struct bpf_func_proto *fn,
+ struct bpf_call_arg_meta *meta)
+{
+ return check_arg_prog_aux(env, fn) &&
check_arg_pair_ok(fn) &&
check_mem_arg_rw_flag_ok(fn) &&
check_proto_release_reg(fn, meta) &&
@@ -9393,6 +10982,20 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
}));
}
+static bool holds_pkt_pointers(struct bpf_verifier_env *env)
+{
+ struct bpf_func_state *state;
+ struct bpf_reg_state *reg;
+ bool ret = false;
+
+ bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({
+ if (reg_is_pkt_pointer_any(reg) || reg_is_dynptr_slice_pkt(reg))
+ ret = true;
+ }));
+
+ return ret;
+}
+
enum {
AT_PKT_END = -1,
BEYOND_PKT_END = -2,
@@ -9587,7 +11190,8 @@ static int ref_convert_alloc_rcu_protected(struct bpf_verifier_env *env, u32 id)
continue;
if ((reg->type & MEM_ALLOC) && (reg->type & MEM_PERCPU)) {
bpf_diag_mod_begin(env, reg, NULL, BPF_DIAG_MOD_WRITE);
- reg->id = 0;
+ if (!type_may_be_null(reg->type))
+ reg->id = 0;
reg->type &= ~MEM_ALLOC;
reg->type |= MEM_RCU;
bpf_diag_mod_end(env);
@@ -9679,23 +11283,57 @@ err_out:
return err;
}
-static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
- const struct btf *btf,
- struct bpf_reg_state *regs)
+static void gen_subprog_arg_proto(const struct bpf_subprog_info *sub, const struct btf *btf,
+ const struct btf_type *func_proto, struct bpf_func_proto *proto)
+{
+ const struct btf_param *args = btf_params(func_proto);
+ u32 arg, slot = 0;
+
+ memset(proto, 0, sizeof(*proto));
+ for (arg = 0; arg < btf_type_vlen(func_proto); arg++) {
+ enum bpf_arg_type arg_type = sub->args[slot].arg_type;
+ const struct btf_type *t;
+
+ if (arg_type & PTR_UNTRUSTED) {
+ /*
+ * An __arg_untrusted argument accepts any caller value. The
+ * callee treats it as read-only and uses probe-read instructions
+ * to protect against invalid memory access.
+ */
+ arg_type = ARG_IGNORE;
+ } else if (base_type(arg_type) == ARG_PTR_TO_ARENA) {
+ arg_type |= PTR_MAYBE_NULL;
+ } else if (base_type(arg_type) == ARG_PTR_TO_MEM) {
+ proto->arg_size[arg] = sub->args[slot].mem_size;
+ arg_type |= MEM_FIXED_SIZE | MEM_WRITE;
+ } else if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
+ proto->arg_btf_id[arg] = &sub->args[slot].btf_id;
+ }
+ proto->arg_type[arg] = arg_type;
+ t = btf_type_skip_modifiers(btf, args[arg].type, NULL);
+ slot += btf_arg_slots(t);
+ }
+}
+
+static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, struct btf *btf)
{
struct bpf_subprog_info *sub = subprog_info(env, subprog);
struct bpf_func_state *caller = cur_func(env);
- struct bpf_verifier_log *log = &env->log;
- struct ref_obj_desc ref_obj = {};
- const struct btf_param *args;
const struct btf_type *func, *func_proto;
- u32 i;
+ struct bpf_call_arg_meta meta;
+ struct bpf_func_proto *fn;
int ret, err;
+ u32 i;
+
+ memset(&meta, 0, sizeof(meta));
+ meta.btf = btf;
+ meta.subprog_may_change_pkt = sub->changes_pkt_data;
+ meta.func_name = bpf_subprog_name(env, subprog);
ret = btf_prepare_func_args(env, subprog);
if (ret) {
if (bpf_in_stack_arg_cnt(sub) > 0) {
- err = check_outgoing_stack_args(env, caller, sub->arg_cnt,
+ err = check_outgoing_stack_args(env, caller, sub->arg_slot_cnt,
bpf_subprog_name(env, subprog),
NULL, NULL);
if (err)
@@ -9706,125 +11344,33 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
func = btf_type_by_id(btf, env->prog->aux->func_info[subprog].type_id);
func_proto = btf_type_by_id(btf, func->type);
- args = btf_params(func_proto);
- ret = check_outgoing_stack_args(env, caller, sub->arg_cnt,
- bpf_subprog_name(env, subprog), btf, args);
- if (ret)
- return ret;
- /* check that BTF function arguments match actual types that the
- * verifier sees.
- */
- for (i = 0; i < sub->arg_cnt; i++) {
- argno_t argno = argno_from_arg(i + 1);
- struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
- struct bpf_subprog_arg_info *arg = &sub->args[i];
+ fn = &env->bpf_subprog_scratch;
+ gen_subprog_arg_proto(sub, btf, func_proto, fn);
+ meta.fn = fn;
+ meta.func_proto = func_proto;
- if (arg->arg_type == ARG_ANYTHING) {
- if (reg->type != SCALAR_VALUE) {
- bpf_log(log, "%s is not a scalar\n", reg_arg_name(env, argno));
- return -EINVAL;
- }
- } else if (arg->arg_type & PTR_UNTRUSTED) {
- /*
- * Anything is allowed for untrusted arguments, as these are
- * read-only and probe read instructions would protect against
- * invalid memory access.
- */
- } else if (arg->arg_type == ARG_PTR_TO_CTX) {
- ret = check_func_arg_reg_off(env, reg, argno, ARG_PTR_TO_CTX);
- if (ret < 0)
- return ret;
- /* If function expects ctx type in BTF check that caller
- * is passing PTR_TO_CTX.
- */
- if (reg->type != PTR_TO_CTX) {
- bpf_log(log, "%s expects pointer to ctx\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- } else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) {
- ret = check_func_arg_reg_off(env, reg, argno, ARG_DONTCARE);
- if (ret < 0)
- return ret;
- if (check_mem_reg(env, reg, argno, arg->mem_size, BPF_READ | BPF_WRITE, NULL,
- NULL))
- return -EINVAL;
- /*
- * PTR_TO_PACKET get passed as PTR_TO_MEM, preventing
- * us from adjusting bounds tracking info.
- */
- if ((reg_is_pkt_pointer_any(reg) || reg_is_dynptr_slice_pkt(reg)) &&
- sub->changes_pkt_data) {
- bpf_log(log, "%s is a packet pointer, but func#%d may change packet data\n",
- reg_arg_name(env, argno), subprog);
- return -EINVAL;
- }
- if (!(arg->arg_type & PTR_MAYBE_NULL) &&
- (type_may_be_null(reg->type) || bpf_register_is_null(reg))) {
- bpf_log(log, "%s is expected to be non-NULL\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- } else if (base_type(arg->arg_type) == ARG_PTR_TO_ARENA) {
- /*
- * Can pass any value and the kernel won't crash, but
- * only PTR_TO_ARENA or SCALAR make sense. Everything
- * else is a bug in the bpf program. Point it out to
- * the user at the verification time instead of
- * run-time debug nightmare.
- */
- if (reg->type != PTR_TO_ARENA && reg->type != SCALAR_VALUE) {
- bpf_log(log, "%s is not a pointer to arena or scalar.\n",
- reg_arg_name(env, argno));
+ /*
+ * Check if all register arguments declared in BTF are initialized.
+ * If they aren't the BTF is unreliable.
+ */
+ if (!bpf_subprog_is_global(env, subprog)) {
+ for (i = 0; i < min_t(u32, sub->arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS); i++)
+ if (caller->regs[BPF_REG_1 + i].type == NOT_INIT)
return -EINVAL;
- }
- } else if (arg->arg_type == ARG_PTR_TO_DYNPTR) {
- ret = check_func_arg_reg_off(env, reg, argno, ARG_PTR_TO_DYNPTR);
- if (ret)
- return ret;
-
- ret = process_dynptr_func(env, reg, argno, env->insn_idx,
- bpf_subprog_name(env, subprog), arg->arg_type,
- &ref_obj, NULL);
- if (ret)
- return ret;
- } else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
- struct bpf_call_arg_meta meta;
- int err;
-
- if (bpf_register_is_null(reg) && type_may_be_null(arg->arg_type)) {
- err = mark_arg_precision(env, argno);
- if (err)
- return err;
- continue;
- }
-
- memset(&meta, 0, sizeof(meta)); /* leave func_id as zero */
- err = check_reg_type(env, reg, argno, arg->arg_type, &arg->btf_id, &meta,
- bpf_subprog_name(env, subprog));
- err = err ?: check_func_arg_reg_off(env, reg, argno, arg->arg_type);
- if (err)
- return err;
- } else {
- verifier_bug(env, "unrecognized %s type %d",
- reg_arg_name(env, argno), arg->arg_type);
- return -EFAULT;
- }
}
-
- return 0;
+ return check_func_args(env, &meta, env->insn_idx);
}
-/* Compare BTF of a function call with given bpf_reg_state.
+/*
+ * Check that call-site argument states match a subprog's BTF signature.
+ *
* Returns:
* EFAULT - there is a verifier bug. Abort verification.
- * EINVAL - there is a type mismatch or BTF is not available.
+ * Other errors - there is a type mismatch or BTF is not available.
* 0 - BTF matches with what bpf_reg_state expects.
- * Only PTR_TO_CTX and SCALAR_VALUE states are recognized.
*/
-static int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
- struct bpf_reg_state *regs)
+static int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog)
{
struct bpf_prog *prog = env->prog;
struct btf *btf = prog->aux->btf;
@@ -9841,7 +11387,7 @@ static int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
if (prog->aux->func_info_aux[subprog].unreliable)
return -EINVAL;
- err = btf_check_func_arg_match(env, subprog, btf, regs);
+ err = btf_check_func_arg_match(env, subprog, btf);
/* Compiler optimizations can remove arguments from static functions
* or mismatched type can be passed into a global function.
* In such cases mark the function as unreliable from BTF point of view.
@@ -9860,7 +11406,7 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
int err;
caller = state->frame[state->curframe];
- err = btf_check_subprog_call(env, subprog, caller->regs);
+ err = btf_check_subprog_call(env, subprog);
if (err == -EFAULT)
return err;
@@ -9891,8 +11437,10 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
is_async_cb_sleepable(env, insn));
if (IS_ERR(async_cb))
return PTR_ERR(async_cb);
+ if (is_async_cb_sleepable(env, insn))
+ mark_sleepable_async_cb(env);
callee = async_cb->frame[0];
- callee->async_entry_cnt = caller->async_entry_cnt + 1;
+ callee->async_entry_cnt = state->frame[0]->async_entry_cnt + 1;
/* Convert bpf_timer_set_callback() args into timer callback args */
err = set_callee_state_cb(env, caller, callee, insn_idx);
@@ -9902,6 +11450,15 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
return 0;
}
+ /*
+ * The address of the callback might be taken in another function or
+ * read from a map, see check_func_ptr_read(). Like with callx it's
+ * known here what is called.
+ */
+ err = record_callx_edge(env, caller->subprogno, subprog);
+ if (err)
+ return err;
+
/* for callback functions enqueue entry to callback and
* proceed with next instruction within current frame.
*/
@@ -9923,14 +11480,63 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
static int process_bpf_exit_full(struct bpf_verifier_env *env,
bool *do_print_state, bool exception_exit);
-static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
- int *insn_idx)
+/*
+ * Call of a static subprog. The callee is verified in the context of
+ * the caller, hence set up a new frame and continue from the first
+ * instruction of the callee.
+ */
+static int check_static_func_call(struct bpf_verifier_env *env, int subprog,
+ int *insn_idx)
{
struct bpf_verifier_state *state = env->cur_state;
struct bpf_subprog_info *caller_info;
u16 callee_incoming, stack_arg_cnt;
struct bpf_func_state *caller;
+ int err;
+
+ caller = state->frame[state->curframe];
+
+ /*
+ * Track caller's total stack arg count (incoming + max outgoing).
+ * This is needed so the JIT knows how much stack arg space to allocate.
+ */
+ caller_info = &env->subprog_info[caller->subprogno];
+ callee_incoming = bpf_in_stack_arg_cnt(&env->subprog_info[subprog]);
+ stack_arg_cnt = bpf_in_stack_arg_cnt(caller_info) + callee_incoming;
+ if (stack_arg_cnt > caller_info->stack_arg_cnt)
+ caller_info->stack_arg_cnt = stack_arg_cnt;
+
+ /*
+ * For regular function entry setup new frame and continue
+ * from that frame.
+ */
+ err = setup_func_entry(env, subprog, *insn_idx, set_callee_state, state);
+ if (err)
+ return err;
+
+ bpf_diag_record_scrub(env, &caller->regs[BPF_REG_0], BPF_DIAG_MOD_CALLER_SAVED);
+ clear_caller_saved_regs(env, caller->regs);
+
+ /* and go analyze first insn of the callee */
+ *insn_idx = env->subprog_info[subprog].start - 1;
+
+ if (env->log.level & BPF_LOG_LEVEL) {
+ verbose(env, "caller:\n");
+ print_verifier_state(env, state, caller->frameno, true);
+ verbose(env, "callee:\n");
+ print_verifier_state(env, state, state->curframe, true);
+ }
+
+ return 0;
+}
+
+static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
+ int *insn_idx)
+{
+ struct bpf_verifier_state *state = env->cur_state;
+ struct bpf_func_state *caller;
int err, subprog, target_insn;
+ u32 i, nregs;
target_insn = *insn_idx + insn->imm + 1;
subprog = bpf_find_subprog(env, target_insn);
@@ -9939,7 +11545,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return -EFAULT;
caller = state->frame[state->curframe];
- err = btf_check_subprog_call(env, subprog, caller->regs);
+ err = btf_check_subprog_call(env, subprog);
if (err == -EFAULT)
return err;
if (bpf_subprog_is_global(env, subprog)) {
@@ -9958,11 +11564,16 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
if (env->subprog_info[subprog].might_sleep && !in_sleepable_context(env)) {
+ const char *suggestion;
+
verbose(env, "sleepable global function %s() called in %s\n",
sub_name, non_sleepable_context_description(env));
+ if (in_sleepable(env))
+ suggestion = "Move the call outside the critical section, or use a non-sleepable function.";
+ else
+ suggestion = "Mark the program sleepable if the program type allows it, or use a non-sleepable function.";
operation = bpf_diag_fmt(env, "sleepable global function %s()", sub_name);
- bpf_diag_ctx_forbidden(env, *insn_idx, operation,
- "Move the call outside the critical section, or use a non-sleepable function.");
+ bpf_diag_ctx_forbidden(env, *insn_idx, operation, suggestion);
return -EINVAL;
}
@@ -9975,7 +11586,12 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (env->log.level & BPF_LOG_LEVEL)
verbose(env, "Func#%d ('%s') is global and assumed valid.\n",
subprog, sub_name);
- sub_aux->called[in_sleepable_context(env)] = true;
+ if (in_sleepable_context(env))
+ sub_aux->called[BPF_FUNC_CTX_SLEEPABLE] = true;
+ else if (has_rcu_read_lock(env))
+ sub_aux->called[BPF_FUNC_CTX_NON_SLEEPABLE] = true;
+ else
+ sub_aux->called[BPF_FUNC_CTX_NO_RCU_LOCK] = true;
returns_void = subprog_returns_void(env, subprog);
if (env->subprog_info[subprog].changes_pkt_data)
clear_all_pkt_pointers(env);
@@ -9985,11 +11601,41 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
bpf_diag_mod_begin(env, &caller->regs[BPF_REG_0], NULL, BPF_DIAG_MOD_WRITE);
clear_caller_saved_regs(env, caller->regs);
invalidate_outgoing_stack_args(env, cur_func(env));
+ /*
+ * What the callers of a global function hold is not known
+ * while it is verified.
+ */
+ if (!env->subprog_info[subprog].changes_pkt_data &&
+ (state->frame[0]->subprogno || holds_pkt_pointers(env)))
+ sub_aux->called_with_pkt_ptrs = true;
- /* All non-void global functions return a 64-bit SCALAR_VALUE. */
+ /*
+ * A non-void global function returns a 64-bit SCALAR_VALUE in
+ * R0, or a >8 byte SCALAR_VALUE in the R0:R2 register pair.
+ */
if (!returns_void) {
- mark_reg_unknown(env, caller->regs, BPF_REG_0);
+ nregs = bpf_ret_reg_pair(env, subprog) ? 2 : 1;
+ mark_reg_unknown(env, caller->regs, ret_regs[0]);
bpf_diag_mod_end(env);
+ for (i = 1; i < nregs; i++) {
+ bpf_diag_mod_begin(env, &caller->regs[ret_regs[i]], NULL,
+ BPF_DIAG_MOD_WRITE);
+ mark_reg_unknown(env, caller->regs, ret_regs[i]);
+ bpf_diag_mod_end(env);
+ }
+ }
+
+ if (env->subprog_info[subprog].can_unwind) {
+ struct bpf_verifier_state *branch;
+
+ if (!bpf_is_unwind_goto(insn + 1)) {
+ verbose(env, "no unwind_goto after the call of %s()\n", sub_name);
+ return -EINVAL;
+ }
+ branch = push_stack(env, *insn_idx + 1, *insn_idx, false);
+ if (IS_ERR(branch))
+ return PTR_ERR(branch);
+ branch->unwound = true;
}
if (env->subprog_info[subprog].might_throw) {
@@ -10007,37 +11653,69 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return 0;
}
- /*
- * Track caller's total stack arg count (incoming + max outgoing).
- * This is needed so the JIT knows how much stack arg space to allocate.
- */
- caller_info = &env->subprog_info[caller->subprogno];
- callee_incoming = bpf_in_stack_arg_cnt(&env->subprog_info[subprog]);
- stack_arg_cnt = bpf_in_stack_arg_cnt(caller_info) + callee_incoming;
- if (stack_arg_cnt > caller_info->stack_arg_cnt)
- caller_info->stack_arg_cnt = stack_arg_cnt;
+ return check_static_func_call(env, subprog, insn_idx);
+}
- /* for regular function entry setup new frame and continue
- * from that frame.
+/*
+ * callx dst_reg: call a bpf subprog whose address is in dst_reg.
+ *
+ * The address of a subprog is either loaded into a register by ld_imm64 with
+ * src_reg == BPF_PSEUDO_FUNC, or it is read from a frozen read-only map, see
+ * resolve_func_ptrs(). Both are possible for static subprogs only. Hence all
+ * possible callees of callx are discovered by add_subprogs() and are reachable
+ * in the control flow graph before the main verification pass begins.
+ * PTR_TO_FUNC register identifies the callee, so from here on callx is verified
+ * as a direct call of that static subprog.
+ */
+static int check_func_callx(struct bpf_verifier_env *env, struct bpf_insn *insn,
+ int *insn_idx)
+{
+ struct bpf_func_state *caller = cur_func(env);
+ struct bpf_reg_state *reg;
+ const char *reason;
+ int err, subprog;
+
+ err = check_reg_arg(env, insn->dst_reg, SRC_OP);
+ if (err)
+ return err;
+
+ reg = reg_state(env, insn->dst_reg);
+ if (reg->type != PTR_TO_FUNC) {
+ verbose(env, "R%d has type %s, expected func\n", insn->dst_reg,
+ reg_type_str(env, reg->type));
+ reason = bpf_diag_fmt(
+ env, "R%d holds %s, but callx can only call through the address of a static BPF function.",
+ insn->dst_reg, bpf_diag_reg_type_plain(env, reg->type));
+ bpf_diag_register_type(
+ env, *insn_idx, insn->dst_reg, "indirect call through a non-function pointer", reason,
+ "Load the address of a static BPF function into the register before callx.");
+ return -EACCES;
+ }
+
+ /*
+ * Arithmetic on PTR_TO_FUNC is allowed, but only unmodified address
+ * of a subprog can be called.
*/
- err = setup_func_entry(env, subprog, *insn_idx, set_callee_state, state);
+ err = check_ptr_off_reg(env, reg, insn->dst_reg);
if (err)
return err;
- bpf_diag_record_scrub(env, &caller->regs[BPF_REG_0], BPF_DIAG_MOD_CALLER_SAVED);
- clear_caller_saved_regs(env, caller->regs);
+ /* the callx is valid, but only a JIT can execute it */
+ err = require_callx_jit(env);
+ if (err)
+ return err;
- /* and go analyze first insn of the callee */
- *insn_idx = env->subprog_info[subprog].start - 1;
+ /* PTR_TO_FUNC is a pointer to a static subprog */
+ subprog = reg->subprogno;
+ err = btf_check_subprog_call(env, subprog);
+ if (err == -EFAULT)
+ return err;
- if (env->log.level & BPF_LOG_LEVEL) {
- verbose(env, "caller:\n");
- print_verifier_state(env, state, caller->frameno, true);
- verbose(env, "callee:\n");
- print_verifier_state(env, state, state->curframe, true);
- }
+ err = record_callx_edge(env, caller->subprogno, subprog);
+ if (err)
+ return err;
- return 0;
+ return check_static_func_call(env, subprog, insn_idx);
}
int map_set_for_each_callback_args(struct bpf_verifier_env *env,
@@ -10105,6 +11783,19 @@ static int set_map_elem_callback_state(struct bpf_verifier_env *env,
if (err)
return err;
+ switch (map->map_type) {
+ case BPF_MAP_TYPE_HASH:
+ case BPF_MAP_TYPE_PERCPU_HASH:
+ case BPF_MAP_TYPE_LRU_HASH:
+ case BPF_MAP_TYPE_LRU_PERCPU_HASH:
+ case BPF_MAP_TYPE_RHASH:
+ /* Hash tables run the callback under rcu_read_lock(). */
+ callee->in_rcu_callback_fn = true;
+ break;
+ default:
+ break;
+ }
+
callee->in_callback_fn = true;
callee->callback_ret_range = retval_range(0, 1);
return 0;
@@ -10191,6 +11882,8 @@ static int set_find_vma_callback_state(struct bpf_verifier_env *env,
bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
callee->in_callback_fn = true;
+ /* bpf_find_vma() runs the callback under mmap_lock and RCU. */
+ callee->in_rcu_callback_fn = true;
callee->callback_ret_range = retval_range(0, 1);
return 0;
}
@@ -10286,6 +11979,40 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env,
return 0;
}
+static int set_rcu_callback_state(struct bpf_verifier_env *env,
+ struct bpf_func_state *caller,
+ struct bpf_func_state *callee,
+ int insn_idx)
+{
+ struct bpf_map *map_ptr = caller->regs[BPF_REG_2].map_ptr;
+ u32 map_uid = caller->regs[BPF_REG_2].map_uid;
+
+ /*
+ * callback_fn(struct bpf_map *map, void *key, void *value);
+ */
+ callee->regs[BPF_REG_1].type = CONST_PTR_TO_MAP;
+ __mark_reg_known_zero(&callee->regs[BPF_REG_1]);
+ callee->regs[BPF_REG_1].map_ptr = map_ptr;
+ callee->regs[BPF_REG_1].map_uid = map_uid;
+
+ callee->regs[BPF_REG_2].type = PTR_TO_MAP_KEY;
+ __mark_reg_known_zero(&callee->regs[BPF_REG_2]);
+ callee->regs[BPF_REG_2].map_ptr = map_ptr;
+ callee->regs[BPF_REG_2].map_uid = map_uid;
+
+ callee->regs[BPF_REG_3].type = PTR_TO_MAP_VALUE;
+ __mark_reg_known_zero(&callee->regs[BPF_REG_3]);
+ callee->regs[BPF_REG_3].map_ptr = map_ptr;
+ callee->regs[BPF_REG_3].map_uid = map_uid;
+
+ /* unused */
+ bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
+ bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
+ callee->in_async_callback_fn = true;
+ callee->callback_ret_range = retval_range(S32_MIN, S32_MAX);
+ return 0;
+}
+
static bool is_rbtree_lock_required_kfunc(u32 btf_id);
static void account_processed_insn(struct bpf_verifier_env *env)
@@ -10364,11 +12091,15 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
struct bpf_func_state *caller, *callee;
struct bpf_reg_state *r0;
bool in_callback_fn;
+ u32 i, nregs;
int err;
callee = state->frame[state->curframe];
r0 = &callee->regs[BPF_REG_0];
- if (r0->type == PTR_TO_STACK) {
+ nregs = bpf_ret_reg_pair(env, callee->subprogno) ? 2 : 1;
+ for (i = 0; i < nregs; i++) {
+ if (callee->regs[ret_regs[i]].type != PTR_TO_STACK)
+ continue;
/* technically it's ok to return caller's stack pointer
* (or caller's caller's pointer) back to the caller,
* since these pointers are valid. Only current stack
@@ -10380,6 +12111,19 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
}
caller = state->frame[state->curframe - 1];
+ if (bpf_is_unwind_exit(&env->prog->insnsi[*insn_idx])) {
+ /* the helper or the kfunc that called it doesn't expect that */
+ if (callee->in_callback_fn) {
+ verbose(env, "callback cannot unwind\n");
+ return -EINVAL;
+ }
+ if (!bpf_is_unwind_goto(&env->prog->insnsi[callee->callsite + 1])) {
+ verbose(env, "unwind_exit, but no unwind_goto after the call at insn %d\n",
+ callee->callsite);
+ return -EINVAL;
+ }
+ state->unwound = true;
+ }
if (callee->in_callback_fn) {
if (r0->type != SCALAR_VALUE) {
verbose(env, "R0 not a scalar value\n");
@@ -10403,12 +12147,26 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
return -EFAULT;
}
} else {
- /* return to the caller whatever r0 had in the callee */
- bpf_diag_mod_begin(env, &caller->regs[BPF_REG_0], r0, BPF_DIAG_MOD_WRITE);
- caller->regs[BPF_REG_0] = *r0;
- bpf_diag_mod_end(env);
+ /*
+ * return to the caller whatever the callee had in the
+ * return register(s)
+ */
+ for (i = 0; i < nregs; i++) {
+ u32 regno = ret_regs[i];
+
+ bpf_diag_mod_begin(env, &caller->regs[regno], &callee->regs[regno],
+ BPF_DIAG_MOD_WRITE);
+ caller->regs[regno] = callee->regs[regno];
+ bpf_diag_mod_end(env);
+ }
}
+ /* Invalidate callee-local dynptrs and their slices before the frame goes away. */
+ err = destroy_dynptrs_in_stack_slots(env, callee, 0,
+ callee->allocated_stack / BPF_REG_SIZE - 1);
+ if (err)
+ return err;
+
/* for callbacks like bpf_loop or bpf_for_each_map_elem go back to callsite,
* there function call logic would reschedule callback visit. If iteration
* converges is_state_visited() would prune that visit eventually.
@@ -10796,22 +12554,37 @@ int bpf_get_helper_proto(struct bpf_verifier_env *env, int func_id,
return *ptr && (*ptr)->func ? 0 : -EINVAL;
}
+/* Is any frame on the current path a callback that runs under RCU? */
+static bool in_rcu_callback(struct bpf_verifier_env *env)
+{
+ struct bpf_verifier_state *state = env->cur_state;
+ int i;
+
+ for (i = 0; i <= state->curframe; i++)
+ if (state->frame[i]->in_rcu_callback_fn)
+ return true;
+ return false;
+}
+
/* Check if we're in a sleepable context. */
static inline bool in_sleepable_context(struct bpf_verifier_env *env)
{
- return !in_rcu_cs(env);
+ return !in_rcu_cs(env) && !in_rcu_callback(env);
}
static const char *non_sleepable_context_description(struct bpf_verifier_env *env)
{
- if (env->cur_state->active_rcu_locks)
- return "rcu_read_lock region";
- if (env->cur_state->active_preempt_locks)
- return "non-preemptible region";
- if (env->cur_state->active_irq_id)
- return "IRQ-disabled region";
- if (env->cur_state->active_locks)
- return "lock region";
+ if (in_sleepable(env)) {
+ if (env->cur_state->active_rcu_locks)
+ return "rcu_read_lock region";
+ if (env->cur_state->active_preempt_locks)
+ return "non-preemptible region";
+ if (env->cur_state->active_irq_id)
+ return "IRQ-disabled region";
+ if (env->cur_state->active_locks)
+ return "lock region";
+ return "callback running under rcu_read_lock";
+ }
return "non-sleepable prog";
}
@@ -10896,19 +12669,24 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
memset(&meta, 0, sizeof(meta));
- err = check_func_proto(fn, &meta);
+ err = check_func_proto(env, fn, &meta);
if (err) {
verifier_bug(env, "incorrect func proto %s#%d", func_id_name(func_id), func_id);
return err;
}
if (fn->might_sleep && !in_sleepable_context(env)) {
+ const char *suggestion;
+
verbose(env, "sleepable helper %s#%d in %s\n", func_id_name(func_id), func_id,
non_sleepable_context_description(env));
+ if (in_sleepable(env))
+ suggestion = "Move the helper call outside the critical section, or use a non-sleepable helper.";
+ else
+ suggestion = "Mark the program sleepable if the program type allows it, or use a non-sleepable helper.";
operation = bpf_diag_fmt(env, "sleepable helper %s#%d",
func_id_name(func_id), func_id);
- bpf_diag_ctx_forbidden(env, insn_idx, operation,
- "Move the helper call outside the critical section, or use a non-sleepable helper.");
+ bpf_diag_ctx_forbidden(env, insn_idx, operation, suggestion);
return -EINVAL;
}
@@ -10917,13 +12695,11 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
env->insn_aux_data[insn_idx].non_sleepable = true;
meta.func_id = func_id;
+ meta.func_name = func_id_name(func_id);
meta.fn = fn;
- /* check args */
- for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
- err = check_func_arg(env, i, &meta, insn_idx);
- if (err)
- return err;
- }
+ err = check_func_args(env, &meta, insn_idx);
+ if (err)
+ return err;
err = record_func_map(env, &meta, func_id, insn_idx);
if (err)
@@ -10935,16 +12711,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
regs = cur_regs(env);
- /* Mark slots with STACK_MISC in case of raw mode, stack offset
- * is inferred from register state.
- */
- 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;
- }
+ err = mark_raw_stack(env, &meta, insn_idx);
+ if (err)
+ return err;
if (meta.release_regno) {
struct bpf_reg_state *reg = &regs[meta.release_regno];
@@ -11065,8 +12834,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
if (dynptr_type == BPF_DYNPTR_TYPE_INVALID)
return -EFAULT;
- if (dynptr_type == BPF_DYNPTR_TYPE_SKB ||
- dynptr_type == BPF_DYNPTR_TYPE_SKB_META)
+ if (dynptr_write_changes_pkt_data(dynptr_type))
/* this will trigger clear_all_pkt_pointers(), which will
* invalidate all dynptr slices associated with the skb
*/
@@ -11364,6 +13132,37 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
return 0;
}
+/*
+ * Mark the register(s) holding a @size byte kfunc return value as unknown
+ * scalars and return how many registers that took. Both halves of a register
+ * pair are treated the same way.
+ *
+ * R0 is written in the diagnostic modification scope the caller opened before
+ * the caller-saved scrub, since R0 gets no scrub record of its own and that
+ * scope is what carries its pre-call value into the history. A scope tracks a
+ * single register and does not nest, so close it before recording the rest of
+ * the return value. A value that fits in R0 leaves the caller's scope open for
+ * the caller to close, so a later write to R0 still lands in it.
+ */
+static u32 mark_kfunc_ret_regs(struct bpf_verifier_env *env,
+ struct bpf_reg_state *regs, u32 size)
+{
+ u32 i, nregs = ret_regs_cnt(size);
+
+ mark_reg_unknown(env, regs, ret_regs[0]);
+ if (nregs == 1)
+ return nregs;
+
+ bpf_diag_mod_end(env);
+ for (i = 1; i < nregs; i++) {
+ bpf_diag_mod_begin(env, &regs[ret_regs[i]], NULL, BPF_DIAG_MOD_WRITE);
+ mark_reg_unknown(env, regs, ret_regs[i]);
+ bpf_diag_mod_end(env);
+ }
+
+ return nregs;
+}
+
static bool is_kfunc_acquire(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_ACQUIRE;
@@ -11474,6 +13273,11 @@ static bool is_kfunc_arg_irq_flag(const struct btf *btf, const struct btf_param
return btf_param_match_suffix(btf, arg, "__irq_flag");
}
+static bool is_kfunc_arg_ctx_out(const struct btf *btf, const struct btf_param *arg)
+{
+ return btf_param_match_suffix(btf, arg, "__ctx_out");
+}
+
static bool is_kfunc_arg_arena(const struct btf *btf, const struct btf_param *arg)
{
return btf_param_match_suffix(btf, arg, "__arena__nullable") ||
@@ -11509,7 +13313,8 @@ enum {
KF_ARG_RES_SPIN_LOCK_ID,
KF_ARG_TASK_WORK_ID,
KF_ARG_PROG_AUX_ID,
- KF_ARG_TIMER_ID
+ KF_ARG_TIMER_ID,
+ KF_ARG_RCU_HEAD_ID
};
BTF_ID_LIST(kf_arg_btf_ids)
@@ -11523,6 +13328,7 @@ BTF_ID(struct, bpf_res_spin_lock)
BTF_ID(struct, bpf_task_work)
BTF_ID(struct, bpf_prog_aux)
BTF_ID(struct, bpf_timer)
+BTF_ID(struct, bpf_rcu_head)
static bool __is_kfunc_ptr_arg_type(const struct btf *btf,
const struct btf_param *arg, int type)
@@ -11581,6 +13387,11 @@ static bool is_kfunc_arg_task_work(const struct btf *btf, const struct btf_param
return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_TASK_WORK_ID);
}
+static bool is_kfunc_arg_rcu_head(const struct btf *btf, const struct btf_param *arg)
+{
+ return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_RCU_HEAD_ID);
+}
+
static bool is_kfunc_arg_res_spin_lock(const struct btf *btf, const struct btf_param *arg)
{
return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_RES_SPIN_LOCK_ID);
@@ -11635,13 +13446,39 @@ static bool is_kfunc_arg_implicit(const struct bpf_call_arg_meta *meta, u32 arg_
return argn <= arg_idx;
}
-/* Returns true if struct is composed of scalars, 4 levels of nesting allowed */
-static bool __btf_type_is_scalar_struct(struct bpf_verifier_env *env,
- const struct btf *btf,
- const struct btf_type *t, int rec)
+#define BTF_MEMBER_MAX_DEPTH 4
+#define BTF_MEMBER_PATH_LEN 64
+
+struct btf_member_path {
+ const struct btf_member *member[BTF_MEMBER_MAX_DEPTH];
+ bool in_array[BTF_MEMBER_MAX_DEPTH];
+ const struct btf_type *bad_type;
+ int depth;
+ bool too_deep;
+};
+
+static bool btf_member_kind_allowed(const struct btf *btf, const struct btf_type *t,
+ u32 member_kinds)
+{
+ if ((member_kinds & BTF_MEMBER_SCALAR) && btf_type_is_scalar(t))
+ return true;
+ if ((member_kinds & BTF_MEMBER_ARENA_PTR) && btf_type_is_arena_ptr(btf, t))
+ return true;
+ return false;
+}
+
+/*
+ * Returns true if every member of struct @t is of a kind listed in
+ * @member_kinds, 4 levels of nesting allowed. An array member counts as its
+ * element type.
+ */
+static bool btf_struct_member_walk(struct bpf_verifier_env *env, const struct btf *btf,
+ const struct btf_type *t, u32 member_kinds, int rec,
+ struct btf_member_path *path)
{
const struct btf_type *member_type;
const struct btf_member *member;
+ bool in_array;
u32 i;
if (!btf_type_is_struct(t))
@@ -11650,58 +13487,128 @@ static bool __btf_type_is_scalar_struct(struct bpf_verifier_env *env,
for_each_member(i, t, member) {
const struct btf_array *array;
+ in_array = false;
member_type = btf_type_skip_modifiers(btf, member->type, NULL);
+ /*
+ * Every element of an array is laid out in the value being
+ * returned, so an array counts as its element type however many
+ * dimensions deep that is.
+ */
+ while (btf_type_is_array(member_type)) {
+ array = btf_array(member_type);
+ if (!array->nelems) {
+ member_type = NULL;
+ goto bad_member;
+ }
+ member_type = btf_type_skip_modifiers(btf, array->type, NULL);
+ in_array = true;
+ }
if (btf_type_is_struct(member_type)) {
- if (rec >= 3) {
+ if (rec >= BTF_MEMBER_MAX_DEPTH - 1) {
verbose(env, "max struct nesting depth exceeded\n");
+ if (path)
+ path->too_deep = true;
return false;
}
- if (!__btf_type_is_scalar_struct(env, btf, member_type, rec + 1))
- return false;
- continue;
- }
- if (btf_type_is_array(member_type)) {
- array = btf_array(member_type);
- if (!array->nelems)
- return false;
- member_type = btf_type_skip_modifiers(btf, array->type, NULL);
- if (!btf_type_is_scalar(member_type))
- return false;
+ if (!btf_struct_member_walk(env, btf, member_type, member_kinds,
+ rec + 1, path))
+ goto bad_path;
continue;
}
- if (!btf_type_is_scalar(member_type))
- return false;
+ if (!btf_member_kind_allowed(btf, member_type, member_kinds))
+ goto bad_member;
}
return true;
+
+bad_member:
+ if (path) {
+ path->depth = rec + 1;
+ path->bad_type = member_type;
+ }
+bad_path:
+ if (path && path->depth) {
+ path->member[rec] = member;
+ path->in_array[rec] = in_array;
+ }
+ return false;
}
-enum kfunc_ptr_arg_type {
- KF_ARG_CONST_MEM_SIZE,
- KF_ARG_MEM_SIZE,
- KF_ARG_CONST,
- KF_ARG_CONST_ALLOC_SIZE_OR_ZERO,
- KF_ARG_ANYTHING,
- KF_ARG_PTR_TO_CTX,
- KF_ARG_PTR_TO_ALLOC_BTF_ID, /* Allocated object */
- KF_ARG_PTR_TO_REFCOUNTED_KPTR, /* Refcounted local kptr */
- KF_ARG_PTR_TO_DYNPTR,
- KF_ARG_PTR_TO_ITER,
- KF_ARG_PTR_TO_LIST_HEAD,
- KF_ARG_PTR_TO_LIST_NODE,
- KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */
- KF_ARG_PTR_TO_MEM,
- KF_ARG_PTR_TO_CALLBACK,
- KF_ARG_PTR_TO_RB_ROOT,
- KF_ARG_PTR_TO_RB_NODE,
- KF_ARG_PTR_TO_CONST_STR,
- KF_ARG_CONST_MAP_PTR,
- KF_ARG_PTR_TO_TIMER,
- KF_ARG_PTR_TO_WORKQUEUE,
- KF_ARG_PTR_TO_IRQ_FLAG,
- KF_ARG_PTR_TO_RES_SPIN_LOCK,
- KF_ARG_PTR_TO_TASK_WORK,
- KF_ARG_PTR_TO_ARENA,
-};
+bool btf_struct_is_composed_of(struct bpf_verifier_env *env,
+ const struct btf *btf,
+ const struct btf_type *t, u32 member_kinds)
+{
+ return btf_struct_member_walk(env, btf, t, member_kinds, 0, NULL);
+}
+
+static bool btf_type_is_scalar_struct(struct bpf_verifier_env *env,
+ const struct btf *btf,
+ const struct btf_type *t)
+{
+ return btf_struct_is_composed_of(env, btf, t, BTF_MEMBER_SCALAR);
+}
+
+static int resolve_func_arg_type(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, u32 arg, argno_t argno,
+ struct bpf_call_arg_meta *meta,
+ enum bpf_arg_type *arg_type, u32 *arg_size)
+{
+ const struct btf_param *args;
+ const struct btf_type *ref_t, *resolve_ret;
+ const struct btf *btf;
+ const char *ref_tname;
+ u32 ref_id;
+
+ if (base_type(*arg_type) == ARG_PTR_TO_MAP_VALUE)
+ return resolve_map_arg_type(env, meta, arg_type);
+
+ if (base_type(*arg_type) != ARG_PTR_TO_BTF_ID)
+ return 0;
+
+ if (!is_kfunc(meta) || arg_type_is_release(*arg_type) ||
+ base_type(reg->type) == PTR_TO_BTF_ID ||
+ reg2btf_ids[base_type(reg->type)])
+ return 0;
+
+ args = btf_params(meta->func_proto);
+ ref_id = *meta->fn->arg_btf_id[arg];
+ btf = is_kfunc_arg_map(meta->btf, &args[arg]) ? btf_vmlinux : meta->btf;
+ 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))
+ return 0;
+
+ resolve_ret = btf_resolve_size(btf, ref_t, arg_size);
+ if (IS_ERR(resolve_ret)) {
+ verbose(env, "%s reference type('%s %s') size cannot be determined: %ld\n",
+ reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname,
+ PTR_ERR(resolve_ret));
+ return -EINVAL;
+ }
+ *arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE |
+ (*arg_type & (PTR_MAYBE_NULL | MEM_UNINIT));
+
+ return 0;
+}
+
+static void btf_member_path_str(const struct btf *btf, const struct btf_member_path *path,
+ char *buf, size_t buf_sz)
+{
+ size_t len = 0;
+ int i;
+
+ buf[0] = '\0';
+ for (i = 0; i < path->depth; i++) {
+ const char *name = btf_name_by_offset(btf, path->member[i]->name_off);
+
+ /* An anonymous struct or union has no name to spell. */
+ if (!name || !name[0])
+ continue;
+ len += scnprintf(buf + len, buf_sz - len, "%s%s", len ? "." : "", name);
+ if (path->in_array[i])
+ len += scnprintf(buf + len, buf_sz - len, "[]");
+ }
+}
enum special_kfunc_type {
KF_bpf_obj_new_impl,
@@ -11768,6 +13675,8 @@ enum special_kfunc_type {
KF___bpf_trap,
KF_bpf_task_work_schedule_signal,
KF_bpf_task_work_schedule_resume,
+ KF_bpf_call_rcu,
+ KF_bpf_call_rcu_tasks_trace,
KF_bpf_arena_alloc_pages,
KF_bpf_arena_free_pages,
KF_bpf_session_is_return,
@@ -11858,14 +13767,36 @@ BTF_ID(func, bpf_dynptr_file_discard)
BTF_ID(func, __bpf_trap)
BTF_ID(func, bpf_task_work_schedule_signal)
BTF_ID(func, bpf_task_work_schedule_resume)
+BTF_ID(func, bpf_call_rcu)
+BTF_ID(func, bpf_call_rcu_tasks_trace)
+#if defined(CONFIG_MMU) && defined(CONFIG_64BIT)
BTF_ID(func, bpf_arena_alloc_pages)
BTF_ID(func, bpf_arena_free_pages)
+#else
+BTF_ID_UNUSED
+BTF_ID_UNUSED
+#endif
#ifdef CONFIG_BPF_EVENTS
BTF_ID(func, bpf_session_is_return)
#else
BTF_ID_UNUSED
#endif
+static bool is_bpf_cast_to_kern_ctx_kfunc(const struct bpf_call_arg_meta *meta)
+{
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx]);
+}
+
+static bool is_bpf_dynptr_clone_kfunc(const struct bpf_call_arg_meta *meta)
+{
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_clone]);
+}
+
+static bool is_bpf_iter_css_task_new_kfunc(const struct bpf_call_arg_meta *meta)
+{
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_iter_css_task_new]);
+}
+
static bool is_bpf_obj_new_kfunc(u32 func_id)
{
return func_id == special_kfunc_list[KF_bpf_obj_new] ||
@@ -11911,6 +13842,12 @@ static bool is_bpf_rbtree_add_kfunc(u32 func_id)
func_id == special_kfunc_list[KF_bpf_rbtree_add_impl];
}
+static bool is_call_rcu_kfunc(u32 func_id)
+{
+ return func_id == special_kfunc_list[KF_bpf_call_rcu] ||
+ func_id == special_kfunc_list[KF_bpf_call_rcu_tasks_trace];
+}
+
static bool is_task_work_add_kfunc(u32 func_id)
{
return func_id == special_kfunc_list[KF_bpf_task_work_schedule_signal] ||
@@ -11927,52 +13864,159 @@ static bool is_kfunc_ret_null(struct bpf_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];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_rcu_read_lock]);
}
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];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_rcu_read_unlock]);
}
static bool is_kfunc_bpf_preempt_disable(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_preempt_disable];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_preempt_disable]);
}
static bool is_kfunc_bpf_preempt_enable(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_preempt_enable];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_preempt_enable]);
+}
+
+/* These write to what their first argument, a dynptr, points to */
+BTF_SET_START(dynptr_write_kfuncs)
+BTF_ID(func, bpf_dynptr_copy)
+BTF_ID(func, bpf_dynptr_memset)
+#ifdef CONFIG_BPF_EVENTS
+BTF_ID(func, bpf_probe_read_user_dynptr)
+BTF_ID(func, bpf_probe_read_kernel_dynptr)
+BTF_ID(func, bpf_probe_read_user_str_dynptr)
+BTF_ID(func, bpf_probe_read_kernel_str_dynptr)
+BTF_ID(func, bpf_copy_from_user_dynptr)
+BTF_ID(func, bpf_copy_from_user_str_dynptr)
+BTF_ID(func, bpf_copy_from_user_task_dynptr)
+BTF_ID(func, bpf_copy_from_user_task_str_dynptr)
+#endif
+BTF_SET_END(dynptr_write_kfuncs)
+
+static bool is_kfunc_dynptr_write(struct bpf_call_arg_meta *meta)
+{
+ return meta->btf == btf_vmlinux &&
+ btf_id_set_contains(&dynptr_write_kfuncs, meta->func_id);
}
+/*
+ * A write through the dynptr of an skb may reallocate its head. Before the
+ * arguments are checked nobody knows what kind of dynptr it is.
+ */
bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_xdp_pull_data];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_xdp_pull_data]) ||
+ is_kfunc_dynptr_write(meta);
+}
+
+static u32 kfunc_abi_slots(const struct btf_func_model *fm)
+{
+ const struct bpf_jit_arg_abi *abi = bpf_jit_arg_abi();
+ u8 pos_of_slot[MAX_BPF_FUNC_ARG_SLOTS];
+ u32 i, nslots, slots = 0;
+
+ for (i = 0; i < fm->nr_args; i++)
+ slots += btf_func_model_arg_slots(fm, i);
+
+ if (!abi)
+ return slots;
+
+ nslots = bpf_jit_place_args(abi, fm, pos_of_slot);
+ for (i = 0; i < nslots; i++)
+ if (pos_of_slot[i] + 1 > slots)
+ slots = pos_of_slot[i] + 1;
+
+ return slots;
+}
+
+static u32 __btf_func_arg_align(const struct btf *btf, const struct btf_type *t, int rec)
+{
+ const struct btf_member *member;
+ const struct btf_type *mt;
+ u32 align, i;
+
+ while (btf_type_is_array(t))
+ t = btf_type_skip_modifiers(btf, btf_array(t)->type, NULL);
+
+ if (btf_type_is_int(t))
+ return t->size > BPF_REG_SIZE ? t->size : BPF_REG_SIZE;
+ if (!btf_type_is_struct(t))
+ return BPF_REG_SIZE;
+ if (rec >= BTF_MEMBER_MAX_DEPTH)
+ return 0;
+
+ for_each_member(i, t, member) {
+ mt = btf_type_skip_modifiers(btf, member->type, NULL);
+ align = __btf_func_arg_align(btf, mt, rec + 1);
+ if (!align)
+ return 0;
+ if (align > BPF_REG_SIZE)
+ return 2 * BPF_REG_SIZE;
+ }
+ return BPF_REG_SIZE;
+}
+
+u32 btf_func_arg_align(const struct btf *btf, const struct btf_type *t)
+{
+ return __btf_func_arg_align(btf, t, 0);
}
static int
get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- const struct btf_param *args, int arg, int nargs)
+ const struct btf_param *args, int arg, int nargs, u32 slot,
+ struct bpf_func_proto *proto)
{
- const struct btf_type *t, *ref_t = NULL;
- argno_t argno = argno_from_arg(arg + 1);
+ const struct btf_type *t, *ref_t = NULL, *resolve_ret;
+ const u32 *ref_id_ptr = NULL;
+ argno_t argno = argno_from_arg(slot + 1);
const char *ref_tname = NULL;
+ u32 ref_id, type_size;
int arg_type;
+ proto->arg_btf_id[arg] = NULL;
+
+ if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
+ return ARG_PTR_TO_PROG_AUX;
+
+ if (is_kfunc_arg_ignore(meta->btf, &args[arg]) || is_kfunc_arg_implicit(meta, arg))
+ return ARG_IGNORE;
+
t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
/* Scalar arguments are classified from their BTF suffix/name alone. */
if (btf_type_is_scalar(t)) {
if (is_kfunc_arg_constant(meta->btf, &args[arg]))
- return KF_ARG_CONST;
+ return ARG_CONST_SCALAR;
if (is_kfunc_arg_const_mem_size(meta->btf, &args[arg]))
- return KF_ARG_CONST_MEM_SIZE;
+ return ARG_CONST_MEM_SIZE;
if (is_kfunc_arg_mem_size(meta->btf, &args[arg]))
- return KF_ARG_MEM_SIZE;
+ return ARG_MEM_SIZE;
if (is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdonly_buf_size") ||
is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdwr_buf_size"))
- return KF_ARG_CONST_ALLOC_SIZE_OR_ZERO;
- return KF_ARG_ANYTHING;
+ return ARG_CONST_ALLOC_SIZE_OR_ZERO;
+ return ARG_SCALAR;
+ }
+
+ if (btf_type_is_struct(t)) {
+ if (!t->size || t->size > 2 * BPF_REG_SIZE) {
+ verbose(env,
+ "%s type %s has size %u, only 1 to %d bytes "
+ "can be passed by value\n",
+ reg_arg_name(env, argno), btf_type_str(t), t->size,
+ 2 * BPF_REG_SIZE);
+ return -EINVAL;
+ }
+ if (!btf_type_is_scalar_struct(env, meta->btf, t)) {
+ verbose(env, "%s type %s is not composed of scalars\n",
+ reg_arg_name(env, argno), btf_type_str(t));
+ return -EINVAL;
+ }
+ return ARG_SCALAR;
}
if (!btf_type_is_ptr(t)) {
@@ -11980,54 +14024,87 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
reg_arg_name(env, argno), btf_type_str(t));
return -EINVAL;
}
- ref_t = btf_type_skip_modifiers(meta->btf, t->type, NULL);
+ /* Keep a pointer to the BTF field containing the resolved referent ID. */
+ ref_id_ptr = &t->type;
+ ref_t = btf_type_skip_modifiers(meta->btf, *ref_id_ptr, &ref_id);
+ while (*ref_id_ptr != ref_id)
+ ref_id_ptr = &btf_type_by_id(meta->btf, *ref_id_ptr)->type;
ref_tname = btf_name_by_offset(meta->btf, ref_t->name_off);
/* In this function, we verify the kfunc's BTF as per the argument type,
* leaving the rest of the verification with respect to the register
* type to our caller. When a set of conditions hold in the BTF type of
- * arguments, we resolve it to a known kfunc_ptr_arg_type.
+ * arguments, we resolve it to a known bpf_arg_type.
*/
- if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] ||
- meta->func_id == special_kfunc_list[KF_bpf_session_is_return] ||
- meta->func_id == special_kfunc_list[KF_bpf_session_cookie])
- arg_type = KF_ARG_PTR_TO_CTX;
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_session_is_return]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_session_cookie]))
+ arg_type = ARG_PTR_TO_CTX;
else if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), arg))
- arg_type = KF_ARG_PTR_TO_CTX;
+ arg_type = ARG_PTR_TO_CTX;
else if (is_kfunc_arg_alloc_obj(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_ALLOC_BTF_ID;
+ arg_type = ARG_PTR_TO_ALLOC_BTF_ID;
else if (is_kfunc_arg_refcounted_kptr(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_REFCOUNTED_KPTR;
- else if (is_kfunc_arg_dynptr(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_DYNPTR;
- else if (is_kfunc_arg_iter(meta, arg, &args[arg]))
- arg_type = KF_ARG_PTR_TO_ITER;
+ arg_type = ARG_PTR_TO_REFCOUNTED_KPTR;
+ else if (is_kfunc_arg_dynptr(meta->btf, &args[arg])) {
+ arg_type = ARG_PTR_TO_DYNPTR;
+
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb]))
+ arg_type |= DYNPTR_TYPE_SKB;
+ else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_xdp]))
+ arg_type |= DYNPTR_TYPE_XDP;
+ else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb_meta]))
+ arg_type |= DYNPTR_TYPE_SKB_META;
+ else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_file]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_file_discard]))
+ /* OBJ_RELEASE for the latter comes from KF_RELEASE below */
+ arg_type |= DYNPTR_TYPE_FILE;
+ } else if (is_kfunc_arg_iter(meta, arg, &args[arg]))
+ arg_type = ARG_PTR_TO_ITER;
else if (is_kfunc_arg_list_head(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_LIST_HEAD;
+ arg_type = ARG_PTR_TO_LIST_HEAD;
else if (is_kfunc_arg_list_node(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_LIST_NODE;
+ arg_type = ARG_PTR_TO_LIST_NODE;
else if (is_kfunc_arg_rbtree_root(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_RB_ROOT;
+ arg_type = ARG_PTR_TO_RB_ROOT;
else if (is_kfunc_arg_rbtree_node(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_RB_NODE;
+ arg_type = ARG_PTR_TO_RB_NODE;
else if (is_kfunc_arg_const_str(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_CONST_STR;
+ arg_type = ARG_PTR_TO_CONST_STR;
else if (is_kfunc_arg_const_map(meta->btf, &args[arg]))
- arg_type = KF_ARG_CONST_MAP_PTR;
+ arg_type = ARG_CONST_MAP_PTR;
else if (is_kfunc_arg_map(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_BTF_ID;
+ arg_type = ARG_PTR_TO_BTF_ID;
else if (is_kfunc_arg_wq(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_WORKQUEUE;
+ arg_type = ARG_PTR_TO_WORKQUEUE;
else if (is_kfunc_arg_timer(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_TIMER;
+ arg_type = ARG_PTR_TO_TIMER;
else if (is_kfunc_arg_task_work(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_TASK_WORK;
+ arg_type = ARG_PTR_TO_TASK_WORK;
+ else if (is_kfunc_arg_rcu_head(meta->btf, &args[arg]))
+ arg_type = ARG_PTR_TO_RCU_HEAD;
else if (is_kfunc_arg_irq_flag(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_IRQ_FLAG;
+ arg_type = ARG_PTR_TO_IRQ_FLAG;
else if (is_kfunc_arg_res_spin_lock(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_RES_SPIN_LOCK;
- else if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_CALLBACK;
+ arg_type = ARG_PTR_TO_RES_SPIN_LOCK;
+ else if (is_kfunc_arg_ctx_out(meta->btf, &args[arg])) {
+ if (!btf_type_is_scalar(ref_t)) {
+ verbose(env, "%s __ctx_out argument must point to a scalar\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ resolve_ret = btf_resolve_size(meta->btf, ref_t, &type_size);
+ if (IS_ERR(resolve_ret)) {
+ verbose(env,
+ "%s reference type('%s %s') size cannot be determined: %ld\n",
+ reg_arg_name(env, argno), btf_type_str(ref_t),
+ ref_tname, PTR_ERR(resolve_ret));
+ return -EINVAL;
+ }
+ proto->arg_size[arg] = type_size;
+ arg_type = ARG_PTR_TO_CTX_OUT | MEM_FIXED_SIZE;
+ } else if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
+ arg_type = ARG_PTR_TO_FUNC;
else if (is_kfunc_arg_arena(meta->btf, &args[arg])) {
if (!bpf_jit_supports_arena_args()) {
verbose(env, "JIT does not support kfunc %s() with arena pointer arguments\n",
@@ -12050,20 +14127,20 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
* whether the JIT rebases it to the arena base or preserves NULL.
* The common nullable path below records that verifier property.
*/
- arg_type = KF_ARG_PTR_TO_ARENA;
+ arg_type = ARG_PTR_TO_ARENA;
} else if (arg + 1 < nargs &&
(is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1]))) {
if (!btf_type_is_void(ref_t) && !btf_type_is_scalar(ref_t) &&
- !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0)) {
+ !btf_type_is_scalar_struct(env, meta->btf, ref_t)) {
verbose(env, "%s pointer type %s %s must point to void, scalar, or struct with scalar\n",
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
return -EINVAL;
}
- arg_type = KF_ARG_PTR_TO_MEM;
+ arg_type = ARG_PTR_TO_MEM | MEM_WRITE;
} else if (btf_type_is_struct(ref_t))
- /* A pointer to a struct without a size argument is classified as KF_ARG_PTR_TO_BTF_ID */
- arg_type = KF_ARG_PTR_TO_BTF_ID;
+ /* A pointer to a struct without a size argument is classified as ARG_PTR_TO_BTF_ID */
+ arg_type = ARG_PTR_TO_BTF_ID;
else {
/*
* Otherwise this is a fixed-size memory buffer supported by
@@ -12071,26 +14148,64 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
* scalars. The access size is derived from the pointed-to BTF type.
*/
if (!btf_type_is_scalar(ref_t) &&
- !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0)) {
+ !btf_type_is_scalar_struct(env, meta->btf, ref_t)) {
verbose(env, "%s pointer type %s %s must point to scalar, or struct with scalar\n",
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
return -EINVAL;
}
- arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
+ resolve_ret = btf_resolve_size(meta->btf, ref_t, &type_size);
+ if (IS_ERR(resolve_ret)) {
+ verbose(env,
+ "%s reference type('%s %s') size cannot be determined: %ld\n",
+ reg_arg_name(env, argno), btf_type_str(ref_t),
+ ref_tname, PTR_ERR(resolve_ret));
+ return -EINVAL;
+ }
+ proto->arg_size[arg] = type_size;
+ arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE;
}
+ if (is_kfunc_arg_uninit(meta->btf, &args[arg]))
+ arg_type |= MEM_UNINIT;
+
if (is_kfunc_arg_nullable(meta->btf, &args[arg]))
arg_type |= PTR_MAYBE_NULL;
+ /*
+ * Only the first argument of a KF_RELEASE kfunc releases anything, and
+ * bpf_fetch_kfunc_arg_meta() only ever records BPF_REG_1 for it.
+ */
+ if (is_kfunc_release(meta) && arg == 0)
+ arg_type |= OBJ_RELEASE;
+
+ if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
+ if (is_kfunc_arg_map(meta->btf, &args[arg]))
+ proto->arg_btf_id[arg] = reg2btf_ids[CONST_PTR_TO_MAP];
+ else
+ proto->arg_btf_id[arg] = ref_id_ptr;
+
+ arg_type |= PTR_TRUSTED;
+
+ /*
+ * A KF_RCU kfunc accepts an RCU-protected pointer where it would
+ * otherwise demand a referenced or trusted one. Other argument kinds
+ * have their own provenance requirements and must not inherit MEM_RCU.
+ */
+ if (is_kfunc_rcu(meta))
+ arg_type |= MEM_RCU;
+ }
+
return arg_type;
}
static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- struct bpf_func_proto *proto)
+ const struct btf_func_model *fm, struct bpf_func_proto *proto)
{
+ const struct bpf_jit_arg_abi *abi;
const struct btf *btf = meta->btf;
const struct btf_param *args;
- u32 i, nargs;
+ const struct btf_type *t;
+ u32 i, nargs, slots_used;
int arg_type;
args = (const struct btf_param *)(meta->func_proto + 1);
@@ -12100,54 +14215,84 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
nargs, MAX_BPF_FUNC_ARGS);
return -EINVAL;
}
- if (nargs > MAX_BPF_FUNC_REG_ARGS && !bpf_jit_supports_stack_args()) {
- verbose(env, "JIT does not support kfunc %s() with %d args\n",
- meta->func_name, nargs);
- return -ENOTSUPP;
+
+ for (i = 0, slots_used = 0; i < nargs; i++) {
+ u32 nslots = btf_func_model_arg_slots(fm, i);
+
+ if (nslots > 1) {
+ t = btf_type_skip_modifiers(btf, args[i].type, NULL);
+ if (!btf_func_arg_align(btf, t)) {
+ verbose(env,
+ "Function %s arg#%d type %s nests structs more than "
+ "%d levels deep\n",
+ meta->func_name, i, btf_type_str(t),
+ BTF_MEMBER_MAX_DEPTH);
+ return -EINVAL;
+ }
+ }
+ slots_used += nslots;
}
- for (i = 0; i < nargs; i++) {
- if (is_kfunc_arg_prog_aux(btf, &args[i]) ||
- is_kfunc_arg_ignore(btf, &args[i]) ||
- is_kfunc_arg_implicit(meta, i))
- continue;
+ if (slots_used > MAX_BPF_FUNC_ARGS) {
+ verbose(env, "Function %s needs %d > %d argument slots\n", meta->func_name,
+ slots_used, MAX_BPF_FUNC_ARGS);
+ return -EINVAL;
+ }
+
+ abi = bpf_jit_arg_abi();
+
+ for (i = 0, slots_used = 0; i < nargs; i++) {
+ u32 nslots = btf_func_model_arg_slots(fm, i);
+
+ if (!abi && nslots > 1) {
+ t = btf_type_skip_modifiers(btf, args[i].type, NULL);
+ verbose(env,
+ "Function %s arg#%d type %s cannot be passed at "
+ "argument slot %d on this architecture\n",
+ meta->func_name, i, btf_type_str(t), slots_used);
+ return -EINVAL;
+ }
+ slots_used += nslots;
- arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
+ arg_type = get_kfunc_arg_type(env, meta, args, i, nargs,
+ slots_used - nslots, proto);
if (arg_type < 0)
return arg_type;
proto->arg_type[i] = arg_type;
}
- return 0;
+ if (slots_used > MAX_BPF_FUNC_REG_ARGS && !bpf_jit_supports_stack_args()) {
+ verbose(env, "JIT does not support kfunc %s() with %d argument slots\n",
+ meta->func_name, slots_used);
+ return -ENOTSUPP;
+ }
+
+ return check_arg_prog_aux(env, proto) ? 0 : -EINVAL;
}
-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_call_arg_meta *meta,
- int arg, argno_t argno)
+static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, enum bpf_arg_type arg_type,
+ const struct btf *arg_btf, u32 arg_btf_id,
+ struct bpf_call_arg_meta *meta, int insn_idx)
{
- const struct btf_type *reg_ref_t;
- bool strict_type_match = false;
+ bool taking_projection, struct_same, strict_type_match = false;
+ const struct btf_type *arg_t, *reg_t;
+ const char *arg_tname, *reg_tname;
const struct btf *reg_btf;
- const char *reg_ref_tname;
- bool taking_projection;
- bool struct_same;
- u32 reg_ref_id;
+ u32 reg_btf_id;
if (base_type(reg->type) == PTR_TO_BTF_ID) {
reg_btf = reg->btf;
- reg_ref_id = reg->btf_id;
+ reg_btf_id = reg->btf_id;
} else {
reg_btf = btf_vmlinux;
- reg_ref_id = *reg2btf_ids[base_type(reg->type)];
+ reg_btf_id = *reg2btf_ids[base_type(reg->type)];
}
- /* Enforce strict type matching for calls to kfuncs that are acquiring
- * or releasing a reference, or are no-cast aliases. We do _not_
- * enforce strict matching for kfuncs by default,
+ /*
+ * Enforce strict type matching for arguments that release a reference,
+ * or are no-cast aliases. We do _not_ enforce strict matching by default,
* as we want to enable BPF programs to pass types that are bitwise
* equivalent without forcing them to explicitly cast with something
* like bpf_cast_to_kern_ctx().
@@ -12169,27 +14314,30 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
* btf_struct_ids_match() to walk the struct at the 0th offset, and
* resolve types.
*/
- if ((is_kfunc_release(meta) && reg_is_referenced(env, reg)) ||
- btf_type_ids_nocast_alias(&env->log, reg_btf, reg_ref_id, meta->btf, ref_id))
+ if ((arg_type_is_release(arg_type) && !is_helper_call(meta, BPF_FUNC_sk_release)) ||
+ (is_kfunc(meta) && btf_type_ids_nocast_alias(&env->log, reg_btf, reg_btf_id,
+ arg_btf, arg_btf_id)))
strict_type_match = true;
- WARN_ON_ONCE(is_kfunc_release(meta) && !tnum_is_const(reg->var_off));
+ arg_t = btf_type_skip_modifiers(arg_btf, arg_btf_id, &arg_btf_id);
+ arg_tname = btf_name_by_offset(arg_btf, arg_t->name_off);
+ reg_t = btf_type_skip_modifiers(reg_btf, reg_btf_id, &reg_btf_id);
+ reg_tname = btf_name_by_offset(reg_btf, reg_t->name_off);
+
+ struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_btf_id,
+ reg->var_off.value, arg_btf, arg_btf_id,
+ strict_type_match, !type_is_alloc(reg->type));
- reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, &reg_ref_id);
- reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off);
- struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, reg->var_off.value,
- meta->btf, ref_id, strict_type_match,
- !type_is_alloc(reg->type));
/* 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.
*/
- taking_projection = btf_is_projection_of(ref_tname, reg_ref_tname);
+ taking_projection = is_kfunc(meta) && btf_is_projection_of(arg_tname, reg_tname);
if (!taking_projection && !struct_same) {
- verbose(env, "kernel function %s %s expected pointer to %s %s but %s has a pointer to %s %s\n",
+ 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),
- btf_type_str(ref_t), ref_tname, reg_arg_name(env, argno),
- btf_type_str(reg_ref_t), reg_ref_tname);
+ btf_type_str(arg_t), arg_tname,
+ reg_arg_name(env, argno), btf_type_str(reg_t), reg_tname);
return -EINVAL;
}
return 0;
@@ -12201,15 +14349,15 @@ static int process_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *
int err, spi, kfunc_class = IRQ_NATIVE_KFUNC;
bool irq_save;
- if (meta->func_id == special_kfunc_list[KF_bpf_local_irq_save] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave]) {
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_local_irq_save]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave])) {
irq_save = true;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
kfunc_class = IRQ_LOCK_KFUNC;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_local_irq_restore] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_local_irq_restore]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])) {
irq_save = false;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
kfunc_class = IRQ_LOCK_KFUNC;
} else {
verifier_bug(env, "unknown irq flags kfunc");
@@ -12402,12 +14550,20 @@ static bool is_bpf_rbtree_api_kfunc(u32 btf_id)
btf_id == special_kfunc_list[KF_bpf_rbtree_right];
}
-static bool is_bpf_res_spin_lock_kfunc(u32 btf_id)
+static int get_bpf_res_spin_lock_kfunc_flags(const struct bpf_call_arg_meta *meta)
{
- return btf_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
- btf_id == special_kfunc_list[KF_bpf_res_spin_unlock] ||
- btf_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave] ||
- btf_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore];
+ int flags = PROCESS_RES_LOCK;
+
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
+ flags |= PROCESS_SPIN_LOCK;
+ else if (!is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock]) &&
+ !is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
+ return 0;
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
+ flags |= PROCESS_LOCK_IRQ;
+ return flags;
}
static bool kfunc_spin_allowed(struct bpf_verifier_env *env, s32 func_id, s16 offset)
@@ -12422,6 +14578,55 @@ static bool kfunc_spin_allowed(struct bpf_verifier_env *env, s32 func_id, s16 of
return *kfunc.flags & KF_SPINLOCK_SAFE;
}
+/*
+ * True if insn calls a helper/kfunc that requires one of its arguments to
+ * be a stack pointer with a constant offset.
+ */
+bool bpf_needs_fixed_stack_off(struct bpf_verifier_env *env, int insn_idx)
+{
+ const struct bpf_insn *insn = &env->prog->insnsi[insn_idx];
+ const struct bpf_func_proto *fn;
+ struct bpf_kfunc_desc *desc;
+ u32 *flags, btf_id;
+ int i;
+
+ if (bpf_helper_call(insn)) {
+ if (bpf_get_helper_proto(env, insn->imm, &fn) < 0)
+ return false;
+ } else if (bpf_pseudo_kfunc_call(insn)) {
+ desc = find_kfunc_desc(env->prog, insn->imm, insn->off);
+ if (!desc)
+ return false;
+ fn = &desc->proto;
+ } else {
+ return false;
+ }
+
+ /* Both initialized and uninitialized stack dynptrs need a fixed offset. */
+ for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++)
+ if (arg_type_is_dynptr(fn->arg_type[i]))
+ return true;
+
+ /* vmlinux kfuncs only */
+ if (!bpf_pseudo_kfunc_call(insn) || insn->off != 0)
+ return false;
+ btf_id = insn->imm;
+
+ flags = btf_kfunc_flags(btf_vmlinux, btf_id, env->prog);
+ if (flags && (*flags & (KF_ITER_NEW | KF_ITER_NEXT | KF_ITER_DESTROY)))
+ return true;
+
+ if (btf_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
+ btf_id == special_kfunc_list[KF_bpf_res_spin_unlock] ||
+ btf_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave] ||
+ btf_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore] ||
+ btf_id == special_kfunc_list[KF_bpf_local_irq_save] ||
+ btf_id == special_kfunc_list[KF_bpf_local_irq_restore])
+ return true;
+
+ return false;
+}
+
static bool is_sync_callback_calling_kfunc(u32 btf_id)
{
return is_bpf_rbtree_add_kfunc(btf_id);
@@ -12430,7 +14635,8 @@ static bool is_sync_callback_calling_kfunc(u32 btf_id)
static bool is_async_callback_calling_kfunc(u32 btf_id)
{
return is_bpf_wq_set_callback_kfunc(btf_id) ||
- is_task_work_add_kfunc(btf_id);
+ is_task_work_add_kfunc(btf_id) ||
+ is_call_rcu_kfunc(btf_id);
}
bool bpf_is_throw_kfunc(struct bpf_insn *insn)
@@ -12684,707 +14890,6 @@ static bool check_css_task_iter_allowlist(struct bpf_verifier_env *env)
}
}
-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;
- struct bpf_func_state *caller = cur_func(env);
- struct bpf_reg_state *regs = cur_regs(env);
- const struct btf *btf = meta->btf;
- const struct btf_param *args;
- struct btf_record *rec;
- u32 i, nargs;
- int ret;
-
- args = (const struct btf_param *)(meta->func_proto + 1);
- nargs = btf_type_vlen(meta->func_proto);
-
- ret = check_outgoing_stack_args(env, caller, nargs, func_name, btf, args);
- if (ret)
- return ret;
-
- /* Check that BTF function arguments match actual types that the
- * verifier sees.
- */
- for (i = 0; i < nargs; i++) {
- struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
- const struct btf_type *t, *ref_t, *resolve_ret;
- enum bpf_arg_type arg_type = ARG_DONTCARE;
- argno_t argno = argno_from_arg(i + 1);
- int regno = reg_from_argno(argno);
- bool btf_id_fixed_off_ok = true;
- u32 ref_id = args[i].type, type_size;
- int kf_arg_type = meta->fn->arg_type[i];
-
- if (is_kfunc_arg_prog_aux(btf, &args[i])) {
- /* Reject repeated use bpf_prog_aux */
- if (meta->arg_prog) {
- verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
- return -EFAULT;
- }
- if (regno < 0) {
- verbose(env, "%s prog->aux cannot be a stack argument\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- meta->arg_prog = true;
- cur_aux(env)->arg_prog = regno;
- continue;
- }
-
- if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
- continue;
-
- t = btf_type_skip_modifiers(btf, args[i].type, NULL);
-
- if (btf_type_is_ptr(t)) {
- ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
- }
-
- if (btf_type_is_ptr(t) &&
- (bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
- !type_may_be_null(kf_arg_type)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, args[i].type);
- verbose(env, "Possibly NULL pointer passed to trusted %s\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Add a NULL check and call the kfunc only on the non-NULL path.",
- "the pointer may be NULL, but this kfunc requires a non-NULL value of type %s",
- expected_type);
- return -EACCES;
- }
-
- if (regno == meta->release_regno && !is_kfunc_arg_dynptr(meta->btf, &args[i]) &&
- !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "release kfunc %s expects referenced PTR_TO_BTF_ID passed to %s\n",
- func_name, reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the resource-owning pointer returned by the matching acquire kfunc, and avoid calling the release kfunc after ownership has already been transferred or released.",
- "release kfuncs require a resource-owning value of type %s returned by a matching acquire kfunc",
- expected_type);
- return -EINVAL;
- }
-
- if (reg_is_referenced(env, reg))
- update_ref_obj(&meta->ref_obj, reg);
-
- if (bpf_register_is_null(reg) && type_may_be_null(kf_arg_type)) {
- ret = mark_arg_precision(env, argno);
- if (ret)
- return ret;
- continue;
- }
-
- if (is_kfunc_arg_map(btf, &args[i])) {
- ref_id = *reg2btf_ids[CONST_PTR_TO_MAP];
- ref_t = btf_type_by_id(btf_vmlinux, ref_id);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
- }
-
- switch (base_type(kf_arg_type)) {
- case KF_ARG_CONST:
- case KF_ARG_CONST_MEM_SIZE:
- case KF_ARG_MEM_SIZE:
- case KF_ARG_ANYTHING:
- case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
- case KF_ARG_PTR_TO_ALLOC_BTF_ID:
- case KF_ARG_PTR_TO_BTF_ID:
- case KF_ARG_CONST_MAP_PTR:
- case KF_ARG_PTR_TO_ITER:
- case KF_ARG_PTR_TO_LIST_HEAD:
- case KF_ARG_PTR_TO_LIST_NODE:
- case KF_ARG_PTR_TO_RB_ROOT:
- case KF_ARG_PTR_TO_RB_NODE:
- case KF_ARG_PTR_TO_MEM:
- case KF_ARG_PTR_TO_CALLBACK:
- case KF_ARG_PTR_TO_CONST_STR:
- case KF_ARG_PTR_TO_WORKQUEUE:
- case KF_ARG_PTR_TO_TIMER:
- case KF_ARG_PTR_TO_TASK_WORK:
- case KF_ARG_PTR_TO_IRQ_FLAG:
- case KF_ARG_PTR_TO_RES_SPIN_LOCK:
- case KF_ARG_PTR_TO_ARENA:
- break;
- case KF_ARG_PTR_TO_DYNPTR:
- arg_type = ARG_PTR_TO_DYNPTR;
- break;
- case KF_ARG_PTR_TO_CTX:
- arg_type = ARG_PTR_TO_CTX;
- break;
- case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
- arg_type = ARG_PTR_TO_BTF_ID;
- btf_id_fixed_off_ok = false;
- break;
- default:
- verifier_bug(env, "unknown kfunc arg type %d", kf_arg_type);
- return -EFAULT;
- }
-
- if (regno == meta->release_regno)
- arg_type |= OBJ_RELEASE;
- ret = __check_func_arg_reg_off(env, reg, argno, arg_type,
- btf_id_fixed_off_ok);
- if (ret < 0)
- return ret;
-
- switch (base_type(kf_arg_type)) {
- case KF_ARG_CONST:
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar value for this argument, not a pointer or resource object.",
- "the kfunc expects an integer scalar, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
- ret = process_const_arg(env, reg, argno, meta);
- if (ret < 0) {
- if (ret == -EINVAL)
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
- "the kfunc requires this scalar argument to be a verifier-known constant, but %s is variable on this path",
- reg_arg_name(env, argno));
- return ret;
- }
- break;
- case KF_ARG_ANYTHING:
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar value for this argument, not a pointer or resource object.",
- "the kfunc expects an integer scalar, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- break;
- case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar value for this argument, not a pointer or resource object.",
- "the kfunc expects an integer scalar, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
- if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size"))
- meta->r0_rdonly = true;
- ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
- if (ret < 0) {
- if (ret == -EINVAL)
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a verifier-known constant size for this kfunc buffer argument.",
- "the kfunc uses this argument as a return-buffer size, but %s is invalid or variable on this path",
- reg_arg_name(env, argno));
- return ret;
- }
- break;
- case KF_ARG_PTR_TO_CTX:
- if (reg->type != PTR_TO_CTX) {
- verbose(env, "%s expected pointer to ctx, but got %s\n",
- reg_arg_name(env, argno), reg_type_str(env, reg->type));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the original program context pointer or preserve it before modifying registers.",
- "the kfunc expects a context pointer, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
- if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
- ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
- if (ret < 0)
- return -EINVAL;
- meta->ret_btf_id = ret;
- }
- break;
- case KF_ARG_PTR_TO_ARENA:
- if (reg->type != PTR_TO_ARENA && reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a pointer to arena or scalar\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- break;
- case KF_ARG_PTR_TO_ALLOC_BTF_ID:
- if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
- if (!is_bpf_obj_drop_kfunc(meta->func_id)) {
- verbose(env, "%s expected for bpf_obj_drop()\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- } else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU)) {
- if (!is_bpf_percpu_obj_drop_kfunc(meta->func_id)) {
- verbose(env, "%s expected for bpf_percpu_obj_drop()\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- } else {
- verbose(env, "%s expected pointer to allocated object\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a pointer returned by the matching BPF object allocation path.",
- "the kfunc expects an allocated object pointer, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- if (!reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the owned object pointer before it is released or transferred.",
- "the allocated object pointer in %s must still carry verifier-tracked ownership, but this pointer no longer owns a live resource",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (meta->btf == btf_vmlinux) {
- meta->arg_btf = reg->btf;
- meta->arg_btf_id = reg->btf_id;
- }
- break;
- case KF_ARG_PTR_TO_DYNPTR:
- {
- enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
-
- if (is_kfunc_arg_uninit(btf, &args[i]))
- dynptr_arg_type |= MEM_UNINIT;
-
- if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb]) {
- dynptr_arg_type |= DYNPTR_TYPE_SKB;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp]) {
- dynptr_arg_type |= DYNPTR_TYPE_XDP;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb_meta]) {
- dynptr_arg_type |= DYNPTR_TYPE_SKB_META;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_file]) {
- dynptr_arg_type |= DYNPTR_TYPE_FILE;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_file_discard]) {
- dynptr_arg_type |= DYNPTR_TYPE_FILE | OBJ_RELEASE;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
- (dynptr_arg_type & MEM_UNINIT)) {
- enum bpf_dynptr_type parent_type = meta->dynptr.type;
-
- if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
- verifier_bug(env, "no dynptr type for parent of clone");
- return -EFAULT;
- }
-
- dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
- }
-
- ret = process_dynptr_func(env, reg, argno, insn_idx, func_name,
- dynptr_arg_type, &meta->ref_obj, &meta->dynptr);
- if (ret < 0)
- return ret;
- break;
- }
- case KF_ARG_PTR_TO_ITER:
- if (meta->func_id == special_kfunc_list[KF_bpf_iter_css_task_new]) {
- if (!check_css_task_iter_allowlist(env)) {
- verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
- return -EINVAL;
- }
- }
- ret = process_iter_arg(env, reg, argno, insn_idx, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_LIST_HEAD:
- if (reg->type != PTR_TO_MAP_VALUE &&
- reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to map value or allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
- !reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
- ret = process_kf_arg_ptr_to_list_head(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_RB_ROOT:
- if (reg->type != PTR_TO_MAP_VALUE &&
- reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to map value or allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
- !reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
- ret = process_kf_arg_ptr_to_rbtree_root(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_LIST_NODE:
- if (is_kfunc_arg_nonown_allowed(btf, &args[i]) &&
- type_is_non_owning_ref(reg->type) && !reg_is_referenced(env, reg)) {
- /* Allow bpf_list_front/back return value for
- * __nonown_allowed list-node arguments.
- */
- goto check_ok;
- }
- if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (!reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
-check_ok:
- ret = process_kf_arg_ptr_to_list_node(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_RB_NODE:
- if (is_bpf_rbtree_add_kfunc(meta->func_id)) {
- if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (!reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
- } else {
- if (!type_is_non_owning_ref(reg->type) &&
- !reg_is_referenced(env, reg)) {
- verbose(env, "%s can only take non-owning or refcounted bpf_rb_node pointer\n", func_name);
- return -EINVAL;
- }
- if (in_rbtree_lock_required_cb(env)) {
- verbose(env, "%s not allowed in rbtree cb\n", func_name);
- return -EINVAL;
- }
- }
-
- ret = process_kf_arg_ptr_to_rbtree_node(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_CONST_MAP_PTR:
- if (base_type(reg->type) != CONST_PTR_TO_MAP ||
- type_may_be_null(reg->type)) {
- verbose(env, "pointer in %s isn't map pointer\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- ret = process_map_ptr_arg(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_BTF_ID:
- /* Only base_type is checked, further checks are done here */
- if (base_type(reg->type) == PTR_TO_BTF_ID ||
- reg2btf_ids[base_type(reg->type)]) {
- if (!is_trusted_reg(env, reg) ||
- bpf_type_has_unsafe_modifiers(reg->type)) {
- if (!is_kfunc_rcu(meta)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "%s must be referenced or trusted\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a pointer acquired from a verifier-tracked source, or call this kfunc only inside the required protection if it accepts RCU pointers.",
- "the kfunc requires a trusted or resource-owning pointer to %s, but %s is %s",
- expected_type,
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- if (!is_rcu_reg(reg)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "%s must be a rcu pointer\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Use this kfunc with a pointer that is valid in an RCU read lock region.",
- "the kfunc requires an RCU-protected pointer to %s, but %s is %s",
- expected_type,
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- }
-
- ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i, argno);
- if (ret < 0)
- return ret;
- break;
- }
-
- if (!__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0)) {
- enum bpf_reg_type reg2btf_type = lookup_reg2btf_ids(ref_id);
- const char *expected_type;
-
- verbose(env, "%s is %s expected %s %s",
- reg_arg_name(env, argno), reg_type_str(env, reg->type),
- btf_type_str(ref_t), ref_tname);
- if (reg2btf_type != NOT_INIT)
- verbose(env, " or %s", reg_type_str(env, reg2btf_type));
- verbose(env, "\n");
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a verifier-tracked pointer to the expected kernel object type, not a pointer to stack storage or another memory buffer.",
- "the kfunc expects a pointer to %s, but this argument is %s and cannot be used as that kernel object pointer",
- expected_type,
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
- /*
- * If the register does not contain btf id but the argument type is a pointer to
- * scalar-only struct, allow verifying it as a fixed size memory.
- */
- kf_arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
- fallthrough;
- case KF_ARG_PTR_TO_MEM:
- if (kf_arg_type & MEM_FIXED_SIZE) {
- bool known_memory;
-
- resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
- if (IS_ERR(resolve_ret)) {
- verbose(env, "%s reference type('%s %s') size cannot be determined: %ld\n",
- reg_arg_name(env, argno), btf_type_str(ref_t),
- ref_tname, PTR_ERR(resolve_ret));
- return -EINVAL;
- }
- ret = check_mem_reg(env, reg, argno, type_size, BPF_READ | BPF_WRITE,
- meta, &known_memory);
- if (ret < 0) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- if (known_memory)
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Pass memory with at least the required number of accessible bytes and suitable read and write access.",
- "the kfunc expects %u bytes of memory for %s, but the verifier cannot prove that %s provides a readable and writable range of that size",
- type_size, expected_type,
- bpf_diag_reg_type_plain(env, reg->type));
- else
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Pass stack, map, context, or other verifier-known memory of the expected type and size, not an integer cast to a pointer.",
- "the kfunc expects %u bytes of memory for %s, but it is %s and not verifier-known memory",
- type_size, expected_type,
- bpf_diag_reg_type_plain(env, reg->type));
- return ret;
- }
- }
- break;
- case KF_ARG_CONST_MEM_SIZE:
- ret = process_const_arg(env, reg, argno, meta);
- if (ret < 0) {
- if (ret == -EINVAL)
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
- "the kfunc requires this memory size to be a verifier-known constant, but %s is variable on this path",
- reg_arg_name(env, argno));
- return ret;
- }
- fallthrough;
- case KF_ARG_MEM_SIZE:
- {
- struct bpf_reg_state *buff_reg = get_func_arg_reg(caller, regs, i - 1);
- struct bpf_reg_state *size_reg = reg;
- argno_t buff_argno = argno_from_arg(i);
- enum bpf_mem_size_failure failure;
-
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar length for this memory argument.",
- "the kfunc expects a scalar memory size, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
- if (bpf_register_is_null(buff_reg))
- break;
-
- ret = check_mem_size_reg(env, buff_reg, size_reg, buff_argno, argno,
- BPF_READ | BPF_WRITE, true, meta, &failure);
- if (ret < 0) {
- const char *buff_arg, *size_arg;
-
- buff_arg = bpf_diag_arg_name(env, buff_argno);
- size_arg = bpf_diag_arg_name(env, argno);
- verbose(env, "%s and ", reg_arg_name(env, buff_argno));
- verbose(env, "%s memory, len pair leads to invalid memory access\n",
- reg_arg_name(env, argno));
- if (failure == BPF_MEM_SIZE_FAIL_MEMORY) {
- bpf_diag_call_arg_fmt(env, insn_idx, buff_argno, func_name,
- "Pass a stack, map, context, or other verifier-known memory pointer, and keep the paired length within that object.",
- "it is the memory pointer in a memory/length pair with %s, but %s does not describe verifier-readable memory for the requested length",
- size_arg, buff_arg);
- } else if (failure == BPF_MEM_SIZE_FAIL_SIZE) {
- if (reg_smin(size_reg) < 0)
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
- "the memory size in %s may be negative because its signed minimum is %lld",
- size_arg, reg_smin(size_reg));
- else
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
- "the memory size in %s may reach %llu bytes, but variable memory accesses must stay below %u bytes",
- size_arg, reg_umax(size_reg), BPF_MAX_VAR_SIZ);
- }
- return ret;
- }
- break;
- }
- case KF_ARG_PTR_TO_CALLBACK:
- if (reg->type != PTR_TO_FUNC) {
- verbose(env, "%s expected pointer to func\n", reg_arg_name(env, argno));
- return -EINVAL;
- }
- meta->subprogno = reg->subprogno;
- break;
- case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
- if (!type_is_ptr_alloc_obj(reg->type)) {
- verbose(env, "%s is neither owning or non-owning ref\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an owning or non-owning pointer to a BPF-managed object containing a bpf_refcount field.",
- "the kfunc expects a pointer to a BPF-managed refcounted object, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- if (!type_is_non_owning_ref(reg->type) && reg_is_referenced(env, reg))
- meta->arg_owning_ref = true;
-
- rec = reg_btf_record(reg);
- if (!rec) {
- verifier_bug(env, "Couldn't find btf_record");
- return -EFAULT;
- }
-
- if (rec->refcount_off < 0) {
- verbose(env, "%s doesn't point to a type with bpf_refcount field\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
-
- meta->arg_btf = reg->btf;
- meta->arg_btf_id = reg->btf_id;
- break;
- case KF_ARG_PTR_TO_CONST_STR:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a const string\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a constant string pointer that the verifier recognizes, such as a string stored in a read-only map value.",
- "the kfunc expects a pointer to a constant string stored in verifier-known memory, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- ret = check_arg_const_str(env, reg, argno);
- if (ret)
- return ret;
- break;
- case KF_ARG_PTR_TO_WORKQUEUE:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a map value\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- ret = check_map_field_pointer(env, reg, argno, BPF_WORKQUEUE, &meta->map);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_TIMER:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a map value\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- ret = process_timer_func(env, reg, argno, &meta->map);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_TASK_WORK:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a map value\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- ret = check_map_field_pointer(env, reg, argno, BPF_TASK_WORK, &meta->map);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_IRQ_FLAG:
- if (reg->type != PTR_TO_STACK) {
- verbose(env, "%s doesn't point to an irq flag on stack\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the same stack slot used by bpf_local_irq_save() or bpf_res_spin_lock_irqsave().",
- "the kfunc expects a stack pointer to an IRQ flag slot, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- ret = process_irq_flag(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case KF_ARG_PTR_TO_RES_SPIN_LOCK:
- {
- int flags = PROCESS_RES_LOCK;
-
- if (in_rbtree_lock_required_cb(env)) {
- verbose(env, "can't res_spin_{lock,unlock} in rbtree cb\n");
- return -EACCES;
- }
-
- if (reg->type != PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s doesn't point to map value or allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
-
- if (!is_bpf_res_spin_lock_kfunc(meta->func_id))
- return -EFAULT;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])
- flags |= PROCESS_SPIN_LOCK;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])
- flags |= PROCESS_LOCK_IRQ;
- ret = process_spin_lock(env, reg, argno, flags);
- if (ret < 0)
- return ret;
- break;
- }
- }
- }
-
- return 0;
-}
-
int bpf_fetch_kfunc_arg_meta(struct bpf_verifier_env *env,
s32 func_id,
s16 offset,
@@ -13417,27 +14922,32 @@ int bpf_fetch_kfunc_arg_meta(struct bpf_verifier_env *env,
}
/*
- * Determine how many bytes a helper accesses through a stack pointer at
- * argument position @arg (0-based, corresponding to R1-R5).
- *
- * Returns:
- * > 0 known read access size in bytes
- * 0 doesn't read anything directly
- * S64_MIN unknown
- * < 0 known write access of (-return) bytes
+ * Describe a helper's stack access through argument @arg (0-based, R1-R5).
+ * must_write means the verifier destroys prior state throughout size bytes.
+ * In other words, must_write follows the verifier's model of the
+ * function's behaviour, which is safe to use for is_state_visited() pruning.
*/
-s64 bpf_helper_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn,
- int arg, int insn_idx)
+struct arg_access_info
+bpf_helper_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn,
+ int arg, int insn_idx)
{
struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
+ struct arg_access_info info = {
+ .size = U32_MAX,
+ .may_read = true,
+ .may_write = true,
+ };
const struct bpf_func_proto *fn;
+ enum bpf_access_type access_type;
enum bpf_arg_type at;
- s64 size;
+ bool exact_size = true;
+ u64 size = U32_MAX;
if (bpf_get_helper_proto(env, insn->imm, &fn) < 0)
- return S64_MIN;
+ return info;
at = fn->arg_type[arg];
+ access_type = func_arg_access_type(at);
switch (base_type(at)) {
case ARG_PTR_TO_MAP_KEY:
@@ -13460,6 +14970,10 @@ s64 bpf_helper_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn
i = aux->const_reg_vals[map_reg];
if (i < env->used_map_cnt) {
+ /* Bloom-filter peek reads the value buffer. */
+ if (!is_key && insn->imm == BPF_FUNC_map_peek_elem &&
+ env->used_maps[i]->map_type == BPF_MAP_TYPE_BLOOM_FILTER)
+ access_type = BPF_READ;
size = is_key ? env->used_maps[i]->key_size
: env->used_maps[i]->value_size;
goto out;
@@ -13469,7 +14983,12 @@ scan_all_maps:
* Map pointer is not known at this call site (e.g. different
* maps on merged paths). Conservatively return the largest
* key_size or value_size across all maps used by the program.
+ * This is only an upper bound, so it cannot establish a definite
+ * write. Map-dependent argument types can also turn an output
+ * into an input, so conservatively retain a read dependency.
*/
+ exact_size = false;
+ access_type |= BPF_READ;
val = 0;
for (i = 0; i < env->used_map_cnt; i++) {
struct bpf_map *map = env->used_maps[i];
@@ -13485,7 +15004,7 @@ scan_all_maps:
}
}
if (!val)
- return S64_MIN;
+ return info;
size = val;
goto out;
}
@@ -13499,18 +15018,12 @@ scan_all_maps:
int size_reg = BPF_REG_1 + arg + 1;
if (aux->const_reg_mask & BIT(size_reg)) {
- size = (s64)aux->const_reg_vals[size_reg];
+ size = aux->const_reg_vals[size_reg];
goto out;
}
- /*
- * Size arg is const on each path but differs across merged
- * paths. MAX_BPF_STACK is a safe upper bound for reads.
- */
- if (at & MEM_UNINIT)
- return 0;
- return MAX_BPF_STACK;
}
- return S64_MIN;
+ /* Preserve access directions even when the extent is unknown. */
+ goto out;
case ARG_PTR_TO_DYNPTR:
size = BPF_DYNPTR_SIZE;
break;
@@ -13520,71 +15033,86 @@ scan_all_maps:
* doesn't access stack. The callback subprog does and it's
* analyzed separately.
*/
- return 0;
+ return (struct arg_access_info) {};
default:
- return S64_MIN;
+ return info;
}
out:
- /*
- * MEM_UNINIT args are write-only: the helper initializes the
- * buffer without reading it.
- */
- if (at & MEM_UNINIT)
- return -size;
- return size;
+ info.size = min_t(u64, size, U32_MAX);
+ info.may_read = !!(access_type & BPF_READ);
+ info.may_write = !!(access_type & BPF_WRITE);
+ info.must_write = info.may_write && exact_size && info.size != U32_MAX;
+ /* Generic unprivileged outputs retain prior initialization state. */
+ if (!env->allow_uninit_stack && arg_type_is_raw_mem(at)) {
+ info.may_read = true;
+ info.must_write = false;
+ }
+ return info;
}
/*
- * Determine how many bytes a kfunc accesses through a stack pointer at
- * argument position @arg (0-based, corresponding to R1-R5).
- *
- * Returns:
- * > 0 known read access size in bytes
- * 0 doesn't access memory through that argument (ex: not a pointer)
- * S64_MIN unknown
- * < 0 known write access of (-return) bytes
+ * Describe a kfunc's stack access through argument slot @arg (0-based).
+ * As for helpers, must_write describes destruction of prior verifier state,
+ * rather than a guarantee that the callee writes every byte at runtime.
*/
-s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn,
- int arg, int insn_idx)
+struct arg_access_info
+bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn,
+ int arg, int insn_idx)
{
struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
+ struct arg_access_info info = {
+ .size = U32_MAX,
+ .may_read = true,
+ .may_write = true,
+ };
struct bpf_call_arg_meta meta;
const struct btf_param *args;
const struct btf_type *t, *ref_t;
const struct btf *btf;
- u32 nargs, type_size;
- s64 size;
+ u32 i, slot, nargs, type_size;
+ u64 size;
if (bpf_fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta) < 0)
- return S64_MIN;
+ return info;
btf = meta.btf;
args = btf_params(meta.func_proto);
nargs = btf_type_vlen(meta.func_proto);
- if (arg >= nargs)
- return 0;
- t = btf_type_skip_modifiers(btf, args[arg].type, NULL);
+ /*
+ * @arg is an argument slot and a 16-byte parameter takes two of them,
+ * so walk the parameters to find the one that starts at this slot. A
+ * slot holding the upper eightbyte of such a parameter belongs to no
+ * pointer, and neither does a slot past the last parameter.
+ */
+ for (i = 0, slot = 0; i < nargs && slot < arg; i++)
+ slot += btf_arg_slots(btf_type_skip_modifiers(btf, args[i].type, NULL));
+ if (i >= nargs || slot != arg)
+ return (struct arg_access_info) {};
+
+ t = btf_type_skip_modifiers(btf, args[i].type, NULL);
if (!btf_type_is_ptr(t))
- return 0;
+ return (struct arg_access_info) {};
/* dynptr: fixed 16-byte on-stack representation */
- if (is_kfunc_arg_dynptr(btf, &args[arg])) {
+ if (is_kfunc_arg_dynptr(btf, &args[i])) {
size = BPF_DYNPTR_SIZE;
goto out;
}
- /* ptr + __sz/__szk pair: size is in the next register */
- if (arg + 1 < nargs &&
- (btf_param_match_suffix(btf, &args[arg + 1], "__sz") ||
- btf_param_match_suffix(btf, &args[arg + 1], "__szk"))) {
+ /* ptr + __sz/__szk pair: the size follows the pointer */
+ if (i + 1 < nargs &&
+ (btf_param_match_suffix(btf, &args[i + 1], "__sz") ||
+ btf_param_match_suffix(btf, &args[i + 1], "__szk"))) {
int size_reg = BPF_REG_1 + arg + 1;
- if (aux->const_reg_mask & BIT(size_reg)) {
- size = (s64)aux->const_reg_vals[size_reg];
+ if (size_reg <= MAX_BPF_FUNC_REG_ARGS &&
+ (aux->const_reg_mask & BIT(size_reg))) {
+ size = aux->const_reg_vals[size_reg];
goto out;
}
- return MAX_BPF_STACK;
+ /* Unknown size: the read may extend anywhere up to the frame top. */
+ return info;
}
/* fixed-size pointed-to type: resolve via BTF */
@@ -13594,14 +15122,99 @@ s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *
goto out;
}
- return S64_MIN;
+ return info;
out:
+ info.size = min_t(u64, size, U32_MAX);
/* KF_ITER_NEW kfuncs initialize the iterator state at arg 0 */
if (arg == 0 && meta.kfunc_flags & KF_ITER_NEW)
- return -size;
- if (is_kfunc_arg_uninit(btf, &args[arg]))
- return -size;
- return size;
+ info.may_read = false;
+ if (is_kfunc_arg_uninit(btf, &args[i]) &&
+ (is_kfunc_arg_dynptr(btf, &args[i]) || env->allow_uninit_stack))
+ info.may_read = false;
+ info.must_write = !info.may_read && info.size != U32_MAX;
+ return info;
+}
+
+/*
+ * Called before dead code elimination, some subprograms may be genuinely unreachable.
+ * Suppress diagnostics so callers can use a conservative result on error.
+ * The main pass would report errors for reachable subprograms.
+ */
+static bool try_btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
+{
+ struct bpf_prog *prog = env->prog;
+ u32 log_level = env->log.level;
+ int err;
+
+ if (!prog->aux->func_info || !prog->aux->func_info[subprog].type_id)
+ return false;
+
+ env->log.level = 0;
+ err = btf_prepare_func_args(env, subprog);
+ env->log.level = log_level;
+ return !err;
+}
+
+/* Prepare argument metadata for liveness; the main program is handled separately. */
+static void prepare_subprog_args(struct bpf_verifier_env *env)
+{
+ int i;
+
+ for (i = 1; i < env->subprog_cnt; i++) {
+ if (!try_btf_prepare_func_args(env, i) && bpf_subprog_is_global(env, i))
+ /* Keep all argument registers live if the prototype is unavailable. */
+ env->subprog_info[i].arg_slot_cnt = MAX_BPF_FUNC_REG_ARGS;
+ }
+}
+
+/*
+ * Derive stack effects for a global subprog's argument slot @arg (0-based)
+ * from its declared type, matching the main pass's argument checks.
+ */
+struct arg_access_info
+bpf_global_subprog_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn,
+ int arg, int insn_idx)
+{
+ struct arg_access_info info = {
+ .size = U32_MAX,
+ .may_read = true,
+ .may_write = true,
+ };
+ struct bpf_subprog_info *sub;
+ int callee;
+
+ callee = bpf_find_subprog(env, insn_idx + insn->imm + 1);
+ if (callee < 0)
+ return info;
+ sub = subprog_info(env, callee);
+ if (!try_btf_prepare_func_args(env, callee))
+ return info;
+
+ switch (base_type(sub->args[arg].arg_type)) {
+ case ARG_UNUSED: /* Not an argument slot. */
+ case ARG_SCALAR: /* A scalar, the callee can't dereference it. */
+ return (struct arg_access_info) {};
+ case ARG_PTR_TO_BTF_ID:
+ /*
+ * Trusted arguments cannot be stack pointers.
+ * Untrusted arguments use ARG_IGNORE:
+ * probe reads need no caller stack state, writes are prohibited.
+ */
+ return (struct arg_access_info) {};
+ case ARG_PTR_TO_MEM:
+ /* mem_size is zero for untrusted pointers of unknown size. */
+ if (sub->args[arg].mem_size) {
+ info.size = sub->args[arg].mem_size;
+ info.must_write = true;
+ }
+ return info;
+ case ARG_PTR_TO_DYNPTR:
+ /* The callee may adjust the view, but cannot reinitialize the dynptr. */
+ info.size = BPF_DYNPTR_SIZE;
+ return info;
+ default:
+ return info;
+ }
}
/* check special kfuncs and return:
@@ -13678,7 +15291,7 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg
struct_meta = btf_find_struct_meta(ret_btf, ret_btf_id);
if (is_bpf_percpu_obj_new_kfunc(meta->func_id)) {
- if (!__btf_type_is_scalar_struct(env, ret_btf, ret_t, 0)) {
+ if (!btf_type_is_scalar_struct(env, ret_btf, ret_t)) {
verbose(env, "bpf_percpu_obj_new type ID argument must be of a struct of scalars\n");
return -EINVAL;
}
@@ -13715,12 +15328,12 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg
struct btf_field *field = meta->arg_rbtree_root.field;
mark_reg_graph_node(regs, BPF_REG_0, &field->graph_root);
- } else if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx])) {
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED;
regs[BPF_REG_0].btf = desc_btf;
regs[BPF_REG_0].btf_id = meta->ret_btf_id;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_rdonly_cast]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_rdonly_cast])) {
ret_t = btf_type_by_id(desc_btf, meta->arg_constant.value);
if (!ret_t) {
verbose(env, "Unknown type ID %lld passed to kfunc bpf_rdonly_cast\n",
@@ -13740,8 +15353,8 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg
"kfunc bpf_rdonly_cast type ID argument must be of a struct or void\n");
return -EINVAL;
}
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice] ||
- meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_slice]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_slice_rdwr])) {
enum bpf_type_flag type_flag = get_dynptr_type_flag(meta->dynptr.type);
mark_reg_known_zero(env, regs, BPF_REG_0);
@@ -13756,7 +15369,7 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg
/* PTR_MAYBE_NULL will be added when is_kfunc_ret_null is checked */
regs[BPF_REG_0].type = PTR_TO_MEM | type_flag;
- if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice]) {
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_slice])) {
regs[BPF_REG_0].type |= MEM_RDONLY;
} else {
/* this will set env->seen_direct_write to true */
@@ -13792,7 +15405,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
struct bpf_insn_aux_data *insn_aux;
const char *operation;
int err, insn_idx = *insn_idx_p;
- u32 i, nargs, ptr_type_id;
+ u32 i, proto_slots, ptr_type_id, ret_nregs = 1;
struct bpf_kfunc_desc *desc;
struct btf *desc_btf;
int id;
@@ -13872,11 +15485,20 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
sleepable = bpf_is_kfunc_sleepable(&meta);
- if (sleepable && !in_sleepable(env)) {
- verbose(env, "program must be sleepable to call sleepable kfunc %s\n", func_name);
+ if (sleepable && !in_sleepable_context(env)) {
+ const char *suggestion;
+
+ if (in_sleepable(env)) {
+ verbose(env, "kernel func %s is sleepable within %s\n",
+ func_name, non_sleepable_context_description(env));
+ suggestion = "Move the kfunc call outside the critical section, or use a non-sleepable kfunc.";
+ } else {
+ verbose(env, "program must be sleepable to call sleepable kfunc %s\n",
+ func_name);
+ suggestion = "Mark the program sleepable if the program type allows it, or use a non-sleepable kfunc.";
+ }
operation = bpf_diag_fmt(env, "sleepable kfunc %s", func_name);
- bpf_diag_ctx_forbidden(env, insn_idx, operation,
- "Mark the program sleepable if the program type allows it, or use a non-sleepable kfunc.");
+ bpf_diag_ctx_forbidden(env, insn_idx, operation, suggestion);
return -EACCES;
}
@@ -13885,10 +15507,14 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
insn_aux->non_sleepable = true;
/* Check the arguments */
- err = check_kfunc_args(env, &meta, insn_idx);
+ err = check_func_args(env, &meta, insn_idx);
if (err < 0)
return err;
+ err = mark_raw_stack(env, &meta, insn_idx);
+ if (err)
+ return err;
+
if ((is_bpf_obj_drop_kfunc(meta.func_id) ||
is_bpf_percpu_obj_drop_kfunc(meta.func_id)) && (is_tracing_prog_type(prog_type) ||
/* is_tracing_prog_type() for now doesn't cover non-iterator tracing progs. */
@@ -13934,6 +15560,16 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
}
+ if (is_call_rcu_kfunc(meta.func_id)) {
+ err = push_callback_call(env, insn, insn_idx, meta.subprogno,
+ set_rcu_callback_state);
+ if (err) {
+ verbose(env, "kfunc %s#%d failed callback verification\n",
+ func_name, meta.func_id);
+ return err;
+ }
+ }
+
rcu_lock = is_kfunc_bpf_rcu_read_lock(&meta);
rcu_unlock = is_kfunc_bpf_rcu_read_unlock(&meta);
@@ -13976,15 +15612,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
invalidate_rcu_protected_refs(env);
}
- if (sleepable && !in_sleepable_context(env)) {
- verbose(env, "kernel func %s is sleepable within %s\n",
- func_name, non_sleepable_context_description(env));
- operation = bpf_diag_fmt(env, "sleepable kfunc %s", func_name);
- bpf_diag_ctx_forbidden(env, insn_idx, operation,
- "Move the kfunc call outside the critical section, or use a non-sleepable kfunc.");
- return -EACCES;
- }
-
if (in_rbtree_lock_required_cb(env) && (rcu_lock || rcu_unlock)) {
verbose(env, "Calling bpf_rcu_read_{lock,unlock} in unnecessary rbtree callback\n");
return -EACCES;
@@ -14055,10 +15682,63 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
if (btf_type_is_scalar(t)) {
- mark_reg_unknown(env, regs, BPF_REG_0);
+ ret_nregs = mark_kfunc_ret_regs(env, regs, t->size);
if (meta.btf == btf_vmlinux && (meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
__mark_reg_const_zero(env, &regs[BPF_REG_0]);
+ } else if (btf_type_is_struct(t)) {
+ struct btf_member_path path = {};
+ const char *member_note = "";
+
+ /*
+ * The returned struct comes back as raw register bits modeled
+ * as an unknown scalar, so a pointer member would be laundered
+ * into a scalar and escape provenance and reference tracking.
+ * Only scalars and arena pointers are allowed; see
+ * btf_validate_return_type() in btf.c for why an arena pointer
+ * is safe here.
+ */
+ if (!btf_struct_member_walk(env, desc_btf, t,
+ BTF_MEMBER_SCALAR | BTF_MEMBER_ARENA_PTR, 0, &path)) {
+ verbose(env,
+ "kernel function %s returns %s %s that is not composed of scalars or arena pointers\n",
+ func_name, btf_type_str(t),
+ btf_name_by_offset(desc_btf, t->name_off));
+ if (path.too_deep) {
+ member_note = bpf_diag_fmt(
+ env, " It nests structs more than %d levels deep.",
+ BTF_MEMBER_MAX_DEPTH);
+ } else if (path.depth) {
+ char bad_name[BTF_MEMBER_PATH_LEN];
+
+ btf_member_path_str(desc_btf, &path, bad_name, sizeof(bad_name));
+ if (!path.bad_type) {
+ verbose(env, "member '%s' is a zero-length array\n",
+ bad_name);
+ member_note = bpf_diag_fmt(
+ env, " Its member '%s' is a zero-length array.",
+ bad_name);
+ } else {
+ verbose(env, "member '%s' has type %s\n", bad_name,
+ btf_type_str(path.bad_type));
+ member_note = bpf_diag_fmt(
+ env,
+ " Its member '%s' is %s, not a scalar or an arena pointer.",
+ bad_name, btf_type_str(path.bad_type));
+ }
+ }
+ bpf_diag_program_structure(
+ env, insn_idx, "unsupported kernel function return type",
+ "Call a kernel function that returns only scalars or arena pointers by value.",
+ "%s() returns %s %s by value.%s "
+ "Only kfuncs returning scalar values or arena pointers, or "
+ "structures composed of scalar values or arena pointers are "
+ "supported.",
+ func_name, btf_type_str(t),
+ btf_name_by_offset(desc_btf, t->name_off), member_note);
+ return -EINVAL;
+ }
+ ret_nregs = mark_kfunc_ret_regs(env, regs, t->size);
} else if (btf_type_is_ptr(t)) {
ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id);
err = check_special_kfunc(env, &meta, regs, insn_aux, ptr_type, desc_btf);
@@ -14171,14 +15851,24 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
}
- if (bpf_is_kfunc_pkt_changing(&meta))
+ if (is_kfunc_dynptr_write(&meta) ?
+ dynptr_write_changes_pkt_data(meta.dynptr_write_type) :
+ bpf_is_kfunc_pkt_changing(&meta))
clear_all_pkt_pointers(env);
- nargs = btf_type_vlen(meta.func_proto);
- if (nargs > MAX_BPF_FUNC_REG_ARGS) {
+ /*
+ * These kfuncs write to the head of the skb in place if what they write
+ * to is linear. Like a direct write that takes bpf_unclone_prologue().
+ */
+ if (is_kfunc_dynptr_write(&meta) &&
+ dynptr_write_changes_pkt_data(meta.dynptr_write_type))
+ may_access_direct_pkt_data(env, NULL, BPF_WRITE);
+
+ proto_slots = kfunc_abi_slots(&desc->func_model);
+ if (proto_slots > MAX_BPF_FUNC_REG_ARGS) {
struct bpf_func_state *caller = cur_func(env);
struct bpf_subprog_info *caller_info = &env->subprog_info[caller->subprogno];
- u16 out_stack_arg_cnt = nargs - MAX_BPF_FUNC_REG_ARGS;
+ u16 out_stack_arg_cnt = proto_slots - MAX_BPF_FUNC_REG_ARGS;
u16 stack_arg_cnt = bpf_in_stack_arg_cnt(caller_info) + out_stack_arg_cnt;
if (stack_arg_cnt > caller_info->stack_arg_cnt)
@@ -14189,7 +15879,8 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
* Record R0 before process_iter_next_call() snapshots the alternate
* iterator path's diagnostic position.
*/
- bpf_diag_mod_end(env);
+ if (ret_nregs == 1)
+ bpf_diag_mod_end(env);
if (bpf_is_iter_next_kfunc(&meta)) {
err = process_iter_next_call(env, insn_idx, &meta);
@@ -14266,7 +15957,8 @@ enum {
REASON_STACK = -5,
};
-static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
+static int retrieve_ptr_limit(const struct bpf_verifier_env *env,
+ const struct bpf_reg_state *ptr_reg,
u32 *alu_limit, bool mask_to_left)
{
u32 max = 0, ptr_limit = 0;
@@ -14278,7 +15970,7 @@ static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
* offset where we would need to deal with min/max bounds is
* currently prohibited for unprivileged.
*/
- max = MAX_BPF_STACK + mask_to_left;
+ max = env->stack_limit + mask_to_left;
ptr_limit = -ptr_reg->var_off.value;
break;
case PTR_TO_MAP_VALUE:
@@ -14398,7 +16090,7 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
(opcode == BPF_SUB && !off_is_neg);
}
- err = retrieve_ptr_limit(ptr_reg, &alu_limit, info->mask_to_left);
+ err = retrieve_ptr_limit(env, ptr_reg, &alu_limit, info->mask_to_left);
if (err < 0)
return err;
@@ -14528,7 +16220,7 @@ static int check_stack_access_for_ptr_arithmetic(
return -EACCES;
}
- if (off >= 0 || off < -MAX_BPF_STACK) {
+ if (off >= 0 || off < -(int)env->stack_limit) {
verbose(env, "R%d stack pointer arithmetic goes out of range, "
"prohibited for !root; off=%d\n", regno, off);
return -EACCES;
@@ -14569,6 +16261,24 @@ static int sanitize_check_bounds(struct bpf_verifier_env *env,
return 0;
}
+static void scalar_step_mul(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg)
+{
+ if (tnum_is_const(src_reg->var_off))
+ scalar_step_scale(dst_reg, src_reg->var_off.value);
+ else
+ reg_step_reset(dst_reg);
+}
+
+static void scalar_step_lsh(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg)
+{
+ u64 amount = src_reg->var_off.value;
+
+ if (tnum_is_const(src_reg->var_off) && amount < 64)
+ scalar_step_scale(dst_reg, 1ULL << amount);
+ else
+ reg_step_reset(dst_reg);
+}
+
/* Handles arithmetic on a pointer and a scalar: computes new min/max and var_off.
* Caller should also handle BPF_MOV case separately.
* If we return -EACCES, caller may want to try again treating pointer as a
@@ -14589,8 +16299,14 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn
u32 dst = insn->dst_reg;
const char *reason;
int ret, bounds_ret;
+ bool to_scalar;
dst_reg = &regs[dst];
+ /*
+ * Only += and -= keep a pointer. Any other op makes a number of it,
+ * which is fine when the program may see values of pointers anyway.
+ */
+ to_scalar = opcode != BPF_ADD && opcode != BPF_SUB && env->allow_ptr_leaks;
if ((known && (smin_val != smax_val || umin_val != umax_val)) ||
smin_val > smax_val || umin_val > umax_val) {
@@ -14601,7 +16317,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn
return 0;
}
- if (BPF_CLASS(insn->code) != BPF_ALU64) {
+ if (BPF_CLASS(insn->code) != BPF_ALU64 && !to_scalar) {
/* 32-bit ALU ops on pointers produce (meaningless) scalars */
if (opcode == BPF_SUB && env->allow_ptr_leaks) {
__mark_reg_unknown(env, dst_reg);
@@ -14667,6 +16383,11 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn
return -EACCES;
}
+ if (to_scalar) {
+ __mark_reg_unknown(env, dst_reg);
+ return 0;
+ }
+
/* For 'scalar += pointer', dst_reg inherits the complete pointer
* register state. Individual fields may be adjusted later by pointer
* arithmetic. Callers guarantee that below does not overwrite off_reg.
@@ -14726,6 +16447,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn
* added into the variable offset, and we copy the fixed offset
* from ptr_reg.
*/
+ scalar_step_add(dst_reg, ptr_reg, off_reg);
dst_reg->r64 = cnum64_add(ptr_reg->r64, off_reg->r64);
dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off);
dst_reg->raw = ptr_reg->raw;
@@ -14787,6 +16509,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn
if ((!known && smin_val < 0) || dst_reg->range < 0)
memset(&dst_reg->raw, 0, sizeof(dst_reg->raw));
}
+ reg_step_reset(dst_reg);
break;
case BPF_AND:
case BPF_OR:
@@ -15494,6 +17217,7 @@ static void scalar_byte_swap(struct bpf_reg_state *dst_reg, struct bpf_insn *ins
* Bounds will be re-derived from the new tnum later.
*/
__mark_reg_unbounded(dst_reg);
+ reg_step_reset(dst_reg);
}
/* For bswap16/32, truncate dst register to match the swapped size */
if (insn->imm == 16 || insn->imm == 32)
@@ -15620,6 +17344,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
*/
switch (opcode) {
case BPF_ADD:
+ scalar_step_add(dst_reg, dst_reg, &src_reg);
scalar32_min_max_add(dst_reg, &src_reg);
scalar_min_max_add(dst_reg, &src_reg);
dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off);
@@ -15628,6 +17353,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
scalar32_min_max_sub(dst_reg, &src_reg);
scalar_min_max_sub(dst_reg, &src_reg);
dst_reg->var_off = tnum_sub(dst_reg->var_off, src_reg.var_off);
+ reg_step_reset(dst_reg);
break;
case BPF_NEG:
env->fake_reg[0] = *dst_reg;
@@ -15635,8 +17361,10 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
scalar32_min_max_sub(dst_reg, &env->fake_reg[0]);
scalar_min_max_sub(dst_reg, &env->fake_reg[0]);
dst_reg->var_off = tnum_neg(env->fake_reg[0].var_off);
+ reg_step_reset(dst_reg);
break;
case BPF_MUL:
+ scalar_step_mul(dst_reg, &src_reg);
dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
scalar32_min_max_mul(dst_reg, &src_reg);
scalar_min_max_mul(dst_reg, &src_reg);
@@ -15657,6 +17385,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
scalar_min_max_sdiv(dst_reg, &src_reg);
else
scalar_min_max_udiv(dst_reg, &src_reg);
+ reg_step_reset(dst_reg);
break;
case BPF_MOD:
/* BPF mod specification: x % 0 = x */
@@ -15672,6 +17401,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
scalar_min_max_smod(dst_reg, &src_reg);
else
scalar_min_max_umod(dst_reg, &src_reg);
+ reg_step_reset(dst_reg);
break;
case BPF_AND:
if (tnum_is_const(src_reg.var_off)) {
@@ -15682,6 +17412,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
dst_reg->var_off = tnum_and(dst_reg->var_off, src_reg.var_off);
scalar32_min_max_and(dst_reg, &src_reg);
scalar_min_max_and(dst_reg, &src_reg);
+ reg_step_reset(dst_reg);
break;
case BPF_OR:
if (tnum_is_const(src_reg.var_off)) {
@@ -15692,13 +17423,16 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
dst_reg->var_off = tnum_or(dst_reg->var_off, src_reg.var_off);
scalar32_min_max_or(dst_reg, &src_reg);
scalar_min_max_or(dst_reg, &src_reg);
+ reg_step_reset(dst_reg);
break;
case BPF_XOR:
dst_reg->var_off = tnum_xor(dst_reg->var_off, src_reg.var_off);
scalar32_min_max_xor(dst_reg, &src_reg);
scalar_min_max_xor(dst_reg, &src_reg);
+ reg_step_reset(dst_reg);
break;
case BPF_LSH:
+ scalar_step_lsh(dst_reg, &src_reg);
if (alu32)
scalar32_min_max_lsh(dst_reg, &src_reg);
else
@@ -15709,15 +17443,18 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
scalar32_min_max_rsh(dst_reg, &src_reg);
else
scalar_min_max_rsh(dst_reg, &src_reg);
+ reg_step_reset(dst_reg);
break;
case BPF_ARSH:
if (alu32)
scalar32_min_max_arsh(dst_reg, &src_reg);
else
scalar_min_max_arsh(dst_reg, &src_reg);
+ reg_step_reset(dst_reg);
break;
case BPF_END:
scalar_byte_swap(dst_reg, insn);
+ reg_step_reset(dst_reg);
break;
default:
break;
@@ -15929,6 +17666,112 @@ clear_id:
return 0;
}
+/*
+ * Many checks done by this function are quite conservative.
+ * This is because main verification pass does not maintain
+ * enough information to track object identities for some
+ * of the interesting types, e.g. PTR_TO_MEM.
+ */
+bool bpf_same_memory_origin(const struct bpf_reg_state *reg_a,
+ const struct bpf_reg_state *reg_b)
+{
+ if (reg_a == reg_b)
+ return true;
+ /* Require matching flags and base types. */
+ if (reg_a->type != reg_b->type)
+ return false;
+ /* NULL can't be compared to some base+offset pointer. */
+ if (type_may_be_null(reg_a->type))
+ return false;
+
+ switch (base_type(reg_a->type)) {
+ case PTR_TO_STACK:
+ return reg_a->frameno == reg_b->frameno;
+ case PTR_TO_MAP_VALUE:
+ if (reg_a->map_ptr != reg_b->map_ptr || reg_a->map_uid != reg_b->map_uid)
+ return false;
+ if (reg_a->id && reg_a->id == reg_b->id)
+ return true;
+ /*
+ * A plain single-element array has one stable value address,
+ * but not when BPF_F_INNER_MAP is set, see arraymap.c:array_map_meta_equal().
+ */
+ if (reg_a->map_ptr->map_type == BPF_MAP_TYPE_ARRAY &&
+ reg_a->map_ptr->max_entries == 1 &&
+ !(reg_a->map_ptr->map_flags & BPF_F_INNER_MAP))
+ return true;
+ /*
+ * The rules above can be simplified / relaxed if:
+ * - fresh IDs would always be assigned for map-value lookups;
+ * - direct map value loads would always have and ID of zero.
+ */
+ return false;
+ case PTR_TO_MEM:
+ case PTR_TO_BUF:
+ return reg_a->id && reg_a->id == reg_b->id;
+
+ default:
+ return false;
+ }
+}
+
+int bpf_set_reg_range(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ struct cnum64 r64, struct cnum32 r32, u16 base, u16 step)
+{
+ reg->r64 = r64;
+ reg->r32 = r32;
+ reg->step = step;
+ reg->base = base;
+ reg->var_off = tnum_unknown;
+ reg_bounds_sync(reg); /* this should infer the tnum alignment */
+ return reg_bounds_sanity_check(env, reg, "bpf_set_reg_range");
+}
+
+/*
+ * Checks if pointer and packet metadata matches between two registers.
+ * For use with bpf_reg_union().
+ */
+bool bpf_reg_union_compatible(const struct bpf_reg_state *a, const struct bpf_reg_state *b)
+{
+ if (a->type != b->type)
+ return false;
+ if (a->type == SCALAR_VALUE)
+ return true;
+ return !memcmp(a, b, offsetof(struct bpf_reg_state, var_off)) &&
+ a->id == b->id && a->parent_id == b->parent_id && a->map_uid == b->map_uid;
+}
+
+/*
+ * Extends acc's scalar range to include src's scalar range.
+ * Caller must ensure that other kinds of metadata is compatible.
+ * Caller must clear acc's scalar ID.
+ */
+int bpf_reg_union(struct bpf_verifier_env *env, struct bpf_reg_state *acc,
+ const struct bpf_reg_state *src)
+{
+ u16 base, step;
+
+ if (!bpf_reg_union_compatible(acc, src)) {
+ verifier_bug(env, "union of incompatible registers");
+ return -EFAULT;
+ }
+ acc->r64 = cnum64_union(acc->r64, src->r64);
+ acc->r32 = cnum32_union(acc->r32, src->r32);
+ acc->var_off = tnum_union(acc->var_off, src->var_off);
+
+ /* Retain a common congruence if the bases agree modulo the gcd. */
+ step = gcd(acc->step, src->step);
+ base = acc->base % step;
+ if (base != src->base % step) {
+ reg_step_reset(acc);
+ } else {
+ acc->base = base;
+ acc->step = step;
+ }
+ reg_bounds_sync(acc);
+ return reg_bounds_sanity_check(env, acc, "bpf_reg_union");
+}
+
/* check validity of 32-bit and 64-bit arithmetic operations */
static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
@@ -16198,8 +18041,6 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state *reg2,
u8 opcode, bool is_jmp32);
-static u8 rev_opcode(u8 opcode);
-
/*
* Learn more information about live branches by simulating refinement on both branches.
* regs_refine_cond_op() is sound, so producing ill-formed register bounds for the branch means
@@ -16208,7 +18049,7 @@ static u8 rev_opcode(u8 opcode);
static int simulate_both_branches_taken(struct bpf_verifier_env *env, u8 opcode, bool is_jmp32)
{
/* Fallthrough (FALSE) branch */
- regs_refine_cond_op(&env->false_reg1, &env->false_reg2, rev_opcode(opcode), is_jmp32);
+ regs_refine_cond_op(&env->false_reg1, &env->false_reg2, bpf_rev_opcode(opcode), is_jmp32);
reg_bounds_sync(&env->false_reg1);
reg_bounds_sync(&env->false_reg2);
/*
@@ -16394,7 +18235,7 @@ static int is_scalar_branch_taken(struct bpf_verifier_env *env, struct bpf_reg_s
return simulate_both_branches_taken(env, opcode, is_jmp32);
}
-static int flip_opcode(u32 opcode)
+int bpf_flip_opcode(u32 opcode)
{
/* How can we transform "a <op> b" into "b <op> a"? */
static const u8 opcode_flip[16] = {
@@ -16425,7 +18266,7 @@ static int is_pkt_ptr_branch_taken(struct bpf_reg_state *dst_reg,
pkt = dst_reg;
} else if (dst_reg->type == PTR_TO_PACKET_END) {
pkt = src_reg;
- opcode = flip_opcode(opcode);
+ opcode = bpf_flip_opcode(opcode);
} else {
return -1;
}
@@ -16480,7 +18321,7 @@ static int is_branch_taken(struct bpf_verifier_env *env, struct bpf_reg_state *r
/* arrange that reg2 is a scalar, and reg1 is a pointer */
if (!is_reg_const(reg2, is_jmp32)) {
- opcode = flip_opcode(opcode);
+ opcode = bpf_flip_opcode(opcode);
swap(reg1, reg2);
}
/* and ensure that reg2 is a constant */
@@ -16514,7 +18355,7 @@ static int is_branch_taken(struct bpf_verifier_env *env, struct bpf_reg_state *r
/* Opcode that corresponds to a *false* branch condition.
* E.g., if r1 < r2, then reverse (false) condition is r1 >= r2
*/
-static u8 rev_opcode(u8 opcode)
+u8 bpf_rev_opcode(u8 opcode)
{
switch (opcode) {
case BPF_JEQ: return BPF_JNE;
@@ -16549,7 +18390,7 @@ static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state
case BPF_JGT:
case BPF_JSGE:
case BPF_JSGT:
- opcode = flip_opcode(opcode);
+ opcode = bpf_flip_opcode(opcode);
swap(reg1, reg2);
break;
default:
@@ -16607,7 +18448,7 @@ static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state
* this restriction. I.e., !(r1 & 0x3) means neither bit 0 nor
* bit 1 is set, which we can readily use in adjustments.
*/
- if (!is_power_of_2(val))
+ if (hweight64(val) != 1)
break;
if (is_jmp32) {
t = tnum_or(tnum_subreg(reg1->var_off), tnum_const(val));
@@ -16616,7 +18457,7 @@ static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state
reg1->var_off = tnum_or(reg1->var_off, tnum_const(val));
}
break;
- case BPF_JSET | BPF_X: /* reverse of BPF_JSET, see rev_opcode() */
+ case BPF_JSET | BPF_X: /* reverse of BPF_JSET, see bpf_rev_opcode() */
if (!is_reg_const(reg2, is_jmp32))
swap(reg1, reg2);
if (!is_reg_const(reg2, is_jmp32))
@@ -16626,6 +18467,7 @@ static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state
* violations if we're on a dead branch.
*/
__mark_reg_unbounded(reg1);
+ reg_step_reset(reg1);
if (is_jmp32) {
t = tnum_and(tnum_subreg(reg1->var_off), tnum_const(~val));
reg1->var_off = tnum_with_subreg(reg1->var_off, t);
@@ -16893,10 +18735,12 @@ static void collect_linked_regs(struct bpf_verifier_env *env,
reg = &func->regs[j];
__collect_linked_regs(linked_regs, reg, id, i, j, true);
}
- for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) {
- if (!bpf_is_spilled_reg(&func->stack[j]))
+ for (j = 0; j < bpf_stack_nr_slots(func); j++) {
+ struct bpf_stack_state *ss = bpf_stack_slot(func, j);
+
+ if (!bpf_is_spilled_reg(ss))
continue;
- reg = &func->stack[j].spilled_ptr;
+ reg = &ss->spilled_ptr;
__collect_linked_regs(linked_regs, reg, id, i, j, false);
}
}
@@ -16916,7 +18760,7 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
for (i = 0; i < linked_regs->cnt; ++i) {
e = &linked_regs->entries[i];
reg = e->is_reg ? &vstate->frame[e->frameno]->regs[e->regno]
- : &vstate->frame[e->frameno]->stack[e->spi].spilled_ptr;
+ : &bpf_stack_slot(vstate->frame[e->frameno], e->spi)->spilled_ptr;
if (reg->type != SCALAR_VALUE || reg == known_reg)
continue;
if ((reg->id & ~BPF_ADD_CONST) != (known_reg->id & ~BPF_ADD_CONST))
@@ -16946,6 +18790,7 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
reg->delta = saved_off;
reg->id = saved_id;
+ scalar_step_add(reg, reg, &fake_reg);
scalar32_min_max_add(reg, &fake_reg);
scalar_min_max_add(reg, &fake_reg);
reg->var_off = tnum_add(reg->var_off, fake_reg.var_off);
@@ -16981,6 +18826,23 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
return -EINVAL;
}
+ if (bpf_is_unwind_goto(insn)) {
+ if (!env->cur_state->unwound)
+ return 0;
+ env->cur_state->unwound = false;
+ /*
+ * unwind_exit changed its return address, so the return is
+ * mispredicted and what follows unwind_goto runs speculatively.
+ */
+ if (!env->bypass_spec_v1) {
+ err = sanitize_speculative_path(env, NULL, *insn_idx + 1, *insn_idx);
+ if (err)
+ return err;
+ }
+ *insn_idx += insn->off;
+ return 0;
+ }
+
if (opcode == BPF_JCOND) {
struct bpf_verifier_state *cur_st = env->cur_state, *queued_st, *prev_st;
int idx = *insn_idx;
@@ -17034,7 +18896,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
}
if (insn_flags) {
- err = bpf_push_jmp_history(env, this_branch, insn_flags, 0, 0, 0);
+ err = bpf_push_jmp_history(env, this_branch, insn_flags, 0, 0, NULL, 0);
if (err)
return err;
}
@@ -17104,7 +18966,10 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
* if parent state is created.
*/
if (linked_regs.cnt > 1) {
- err = bpf_push_jmp_history(env, this_branch, 0, 0, 0, linked_regs_pack(&linked_regs));
+ u16 packed[LINKED_REGS_MAX];
+
+ linked_regs_pack(&linked_regs, packed);
+ err = bpf_push_jmp_history(env, this_branch, 0, 0, 0, packed, linked_regs.cnt);
if (err)
return err;
}
@@ -17669,37 +19534,54 @@ enforce_retval:
return 0;
}
-static int check_global_subprog_return_code(struct bpf_verifier_env *env)
+static int check_global_ret_scalar_reg(struct bpf_verifier_env *env, u32 regno)
{
- struct bpf_reg_state *reg = reg_state(env, BPF_REG_0);
- struct bpf_func_state *cur_frame = cur_func(env);
+ struct bpf_reg_state *reg;
int err;
- if (subprog_returns_void(env, cur_frame->subprogno))
- return 0;
-
- err = check_reg_arg(env, BPF_REG_0, SRC_OP);
+ err = check_reg_arg(env, regno, SRC_OP);
if (err)
return err;
/* Pointers to arena are safe to pass between subprograms. */
- if (is_arena_reg(env, BPF_REG_0))
+ if (is_arena_reg(env, regno))
return 0;
- if (is_pointer_value(env, BPF_REG_0)) {
- verbose(env, "R%d leaks addr as return value\n", BPF_REG_0);
+ if (is_pointer_value(env, regno)) {
+ verbose(env, "R%d leaks addr as return value\n", regno);
return -EACCES;
}
+ reg = reg_state(env, regno);
if (reg->type != SCALAR_VALUE) {
- verbose(env, "At subprogram exit the register R0 is not a scalar value (%s)\n",
- reg_type_str(env, reg->type));
+ verbose(env, "At subprogram exit the register R%d is not a scalar value (%s)\n",
+ regno, reg_type_str(env, reg->type));
return -EINVAL;
}
return 0;
}
+static int check_global_subprog_return_code(struct bpf_verifier_env *env)
+{
+ struct bpf_func_state *cur_frame = cur_func(env);
+ u32 subprog = cur_frame->subprogno;
+ u32 i, nregs;
+ int err;
+
+ if (subprog_returns_void(env, subprog))
+ return 0;
+
+ nregs = bpf_ret_reg_pair(env, subprog) ? 2 : 1;
+ for (i = 0; i < nregs; i++) {
+ err = check_global_ret_scalar_reg(env, ret_regs[i]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
/* Bitmask with 1s for all caller saved registers */
#define ALL_CALLER_SAVED_REGS ((1u << CALLER_SAVED_REGS) - 1)
@@ -17741,11 +19623,11 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,
(bpf_verifier_inlines_helper_call(env, call->imm) ||
bpf_jit_inlines_helper_call(call->imm));
cs->is_void = fn->ret_type == RET_VOID;
- cs->num_params = 0;
+ cs->arg_slot_cnt = 0;
for (i = 0; i < ARRAY_SIZE(fn->arg_type); ++i) {
- if (fn->arg_type[i] == ARG_DONTCARE)
+ if (fn->arg_type[i] == ARG_UNUSED)
break;
- cs->num_params++;
+ cs->arg_slot_cnt++;
}
return true;
}
@@ -17757,7 +19639,7 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,
if (err < 0)
/* error would be reported later */
return false;
- cs->num_params = btf_type_vlen(meta.func_proto);
+ cs->arg_slot_cnt = btf_proto_slots(meta.btf, meta.func_proto);
cs->fastcall = meta.kfunc_flags & KF_FASTCALL;
cs->is_void = btf_type_is_void(btf_type_by_id(meta.btf, meta.func_proto->type));
return true;
@@ -17866,7 +19748,7 @@ static void mark_fastcall_pattern_for_call(struct bpf_verifier_env *env,
* - includes R1-R5 if corresponding parameter has is described
* in the function prototype.
*/
- clobbered_regs_mask = GENMASK(cs.num_params, cs.is_void ? 1 : 0);
+ clobbered_regs_mask = GENMASK(cs.arg_slot_cnt, cs.is_void ? 1 : 0);
/* e.g. if helper call clobbers r{0,1}, expect r{2,3,4,5} in the pattern */
expected_regs_mask = ~clobbered_regs_mask & ALL_CALLER_SAVED_REGS;
@@ -18232,10 +20114,25 @@ static int indirect_jump_min_max_index(struct bpf_verifier_env *env,
return 0;
}
+static int reject_gotox_out_of_subprog(struct bpf_verifier_env *env, u32 target,
+ u32 subprog_start, u32 subprog_end)
+{
+ verbose(env, "indirect jump from insn %d to %u leaves the subprog [%u,%u)\n",
+ env->insn_idx, target, subprog_start, subprog_end);
+ bpf_diag_program_structure(
+ env, env->insn_idx, "indirect jump leaves subprogram",
+ "Keep every reachable jump-table target inside the subprogram of the indirect jump.",
+ "Instruction %d can jump indirectly to instruction %u, which is outside its own subprogram [%u,%u).",
+ env->insn_idx, target, subprog_start, subprog_end);
+ return -EINVAL;
+}
+
/* gotox *dst_reg */
static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
struct bpf_verifier_state *other_branch;
+ struct bpf_subprog_info *subprog;
+ u32 subprog_start, subprog_end;
struct bpf_reg_state *dst_reg;
struct bpf_map *map;
u32 min_index, max_index;
@@ -18279,6 +20176,24 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in
return -EINVAL;
}
+ subprog = bpf_find_containing_subprog(env, env->insn_idx);
+ if (verifier_bug_if(!subprog, env, "no subprog contains insn %d", env->insn_idx))
+ return -EFAULT;
+ subprog_start = subprog->start;
+ subprog_end = (subprog + 1)->start;
+
+ /*
+ * Maps are confined to a subprog, see compute_subprog_jts(), and the
+ * targets are sorted and free of duplicates, see sort_insn_array_uniq(),
+ * so either all of them belong to this subprog or none of them does.
+ */
+ if (env->gotox_tmp_buf->items[0] < subprog_start)
+ return reject_gotox_out_of_subprog(env, env->gotox_tmp_buf->items[0],
+ subprog_start, subprog_end);
+ if (env->gotox_tmp_buf->items[n - 1] >= subprog_end)
+ return reject_gotox_out_of_subprog(env, env->gotox_tmp_buf->items[n - 1],
+ subprog_start, subprog_end);
+
for (i = 0; i < n - 1; i++) {
mark_indirect_target(env, env->gotox_tmp_buf->items[i]);
other_branch = push_stack(env, env->gotox_tmp_buf->items[i],
@@ -18321,15 +20236,17 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
return check_stack_arg_write(env, state, insn->off, NULL);
}
+ struct bpf_reg_state arena, *dst_reg;
enum bpf_reg_type dst_reg_type;
err = check_reg_arg(env, insn->dst_reg, SRC_OP);
if (err)
return err;
- dst_reg_type = cur_regs(env)[insn->dst_reg].type;
+ dst_reg = mem_access_reg(env, insn->dst_reg, &arena);
+ dst_reg_type = dst_reg->type;
- err = check_mem_access(env, env->insn_idx, cur_regs(env) + insn->dst_reg, argno_from_reg(insn->dst_reg),
+ err = check_mem_access(env, env->insn_idx, dst_reg, argno_from_reg(insn->dst_reg),
insn->off, BPF_SIZE(insn->code),
BPF_WRITE, -1, false, false);
if (err)
@@ -18344,11 +20261,13 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
env->jmps_processed++;
if (opcode == BPF_CALL) {
if (env->cur_state->active_locks) {
- if ((insn->src_reg == BPF_REG_0 &&
- insn->imm != BPF_FUNC_spin_unlock &&
- insn->imm != BPF_FUNC_kptr_xchg) ||
- (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
- !kfunc_spin_allowed(env, insn->imm, insn->off))) {
+ /* similar to static subprog calls callx is allowed under a lock */
+ if (!bpf_is_callx(insn) &&
+ ((insn->src_reg == BPF_REG_0 &&
+ insn->imm != BPF_FUNC_spin_unlock &&
+ insn->imm != BPF_FUNC_kptr_xchg) ||
+ (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
+ !kfunc_spin_allowed(env, insn->imm, insn->off)))) {
verbose(env,
"function calls are not allowed while holding a lock\n");
bpf_diag_ctx_active(
@@ -18361,6 +20280,8 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
mark_reg_scratched(env, BPF_REG_0);
if (bpf_in_stack_arg_cnt(&env->subprog_info[cur_func(env)->subprogno]))
cur_func(env)->no_stack_arg_load = true;
+ if (bpf_is_callx(insn))
+ return check_func_callx(env, insn, &env->insn_idx);
if (insn->src_reg == BPF_PSEUDO_CALL)
return check_func_call(env, insn, &env->insn_idx);
if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL)
@@ -18401,6 +20322,178 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
return -EFAULT;
}
+/*
+ * Push the loop entered at insn_idx onto the stack. Usually this adds a single
+ * entry on top of its enclosing loop. However, an edge may enter an inner loop
+ * directly, bypassing the header(s) of its enclosing loop(s), e.g.:
+ *
+ * 1: for (...): // enclosing loop, header at 1
+ * 2: for (...): // inner loop, header at 2
+ * ...
+ * 3: if ...:
+ * goto 2b; // enters loop 2 without going through header 1
+ *
+ * Such a bypass makes the enclosing loop irreducible, so its header is missing
+ * from the stack and both headers (1) and (2) need to be pushed onto stack.
+ * Only the innermost loop (the one actually entered at insn_idx) carries SCEV bounds;
+ * the bypassed ancestors are irreducible and pushed as non-terminating.
+ *
+ * Assumes loop_stack_pop() has already truncated the stack to the common ancestor
+ * of the bpf_loop_at_index(env->insn_idx) and whatever was at the top of the loop stack.
+ */
+static int loop_stack_push(struct bpf_verifier_env *env, bool *pushed)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_func_state *frame = cur_func(env);
+ struct loop_stack_entry *loop_stack = frame->loop_stack;
+ u32 missing_headers[LOOP_STACK_SIZE];
+ u32 cnt = frame->loop_stack_cnt;
+ u32 num_missing = 0;
+ int h;
+
+ *pushed = false;
+
+ for (h = bpf_loop_at_index(env, env->insn_idx); h >= 0; h = aux[h].loop_header, num_missing++) {
+ if (cnt && loop_stack[cnt - 1].loop_id == h)
+ break;
+ if (num_missing == LOOP_STACK_SIZE)
+ goto e2big;
+ missing_headers[num_missing] = h;
+ }
+
+ if (num_missing == 0)
+ return 0;
+ if (cnt + num_missing > LOOP_STACK_SIZE)
+ goto e2big;
+ frame->loop_stack = realloc_array(frame->loop_stack, cnt, cnt + num_missing,
+ sizeof(*frame->loop_stack));
+ if (!frame->loop_stack)
+ return -ENOMEM;
+ loop_stack = frame->loop_stack;
+
+ for (; num_missing; num_missing--, cnt++) {
+ h = missing_headers[num_missing - 1];
+ loop_stack[cnt] = (struct loop_stack_entry){ .loop_id = h };
+ if (env->log.level & BPF_LOG_LEVEL2)
+ verbose(env, "entering loop %d\n", h);
+ *pushed = true;
+ }
+ frame->loop_stack_cnt = cnt;
+ return 0;
+
+e2big:
+ verbose(env, "Too many nested loops (%d/%d) at %d\n", cnt, num_missing, env->insn_idx);
+ return -E2BIG;
+}
+
+/*
+ * An exit from a loop can cross several nested loops, e.g.:
+ *
+ * 1: for (...):
+ * 2: for (...):
+ * 3: for (...):
+ * if ...: // before goto the loop stack is [1, 2, 3 <top>]
+ * goto 1b; // after goto it should become [1]
+ */
+static int loop_stack_pop(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
+ struct bpf_func_state *frame = cur_func(env);
+ int new_cnt = 0;
+ int h, i;
+
+ /*
+ * Walk the loop nest of insn_idx outwards (innermost first) and stop at
+ * the first loop that is present on the stack - that loop is the common
+ * ancestor and becomes the new top. If no enclosing loop is on the
+ * stack, drop all loops.
+ */
+ for (h = bpf_loop_at_index(env, env->insn_idx); h >= 0; h = aux[h].loop_header) {
+ for (i = frame->loop_stack_cnt - 1; i >= 0; i--) {
+ if (frame->loop_stack[i].loop_id == h) {
+ new_cnt = i + 1;
+ goto pop;
+ }
+ }
+ }
+
+pop:
+ if (env->log.level & BPF_LOG_LEVEL2)
+ for (i = frame->loop_stack_cnt - 1; i >= new_cnt; i--)
+ verbose(env, "exiting loop %d\n", frame->loop_stack[i].loop_id);
+ frame->loop_stack_cnt = new_cnt;
+ return 0;
+}
+
+static int maybe_clamp_scev_regs(struct bpf_verifier_env *env)
+{
+ struct bpf_func_state *frame = cur_func(env);
+ struct loop_stack_entry *entry;
+
+ if (frame->loop_stack_cnt == 0) {
+ verifier_bug(env, "%s: loop stack empty at %d", __FUNCTION__, env->insn_idx);
+ return -EFAULT;
+ }
+
+ entry = &frame->loop_stack[frame->loop_stack_cnt - 1];
+ if (!entry->terminates)
+ return 0;
+
+ return bpf_clamp_scev_regs(env, frame, env->insn_idx, entry->entry_state,
+ entry->max_header_count);
+}
+
+static int handle_loop_entry_exit(struct bpf_verifier_env *env)
+{
+ struct bpf_verifier_state *cur = env->cur_state;
+ struct bpf_func_state *frame = cur_func(env);
+ struct loop_stack_entry *entry;
+ struct bpf_loop_iters iters;
+ bool pushed, terminates;
+ int err;
+
+ err = loop_stack_pop(env);
+ if (err)
+ return err;
+
+ err = loop_stack_push(env, &pushed);
+ if (err)
+ return err;
+
+ if (frame->loop_stack_cnt == 0)
+ return 0;
+
+ entry = &frame->loop_stack[frame->loop_stack_cnt - 1];
+ if (env->insn_idx != entry->loop_id)
+ return 0;
+
+ /* If nothing was pushed and env->insn_idx is a loop header, we've taken a backedge. */
+ if (!pushed)
+ return maybe_clamp_scev_regs(env);
+
+ err = bpf_compute_loop_iters(env, env->cur_state, &iters);
+ if (err < 0)
+ return err;
+
+ terminates = err == 1;
+ if (!terminates)
+ return 0;
+
+ /* Create a loop_entry checkpoint before widening */
+ err = bpf_split_cur_state(env);
+ if (err)
+ return err;
+
+ entry->min_header_count = iters.min_header_count;
+ entry->max_header_count = iters.max_header_count;
+ entry->entry_state = cur->parent;
+ entry->terminates = true;
+ err = bpf_widen_scev_regs(env, cur, cur->parent, &iters);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
static int do_check(struct bpf_verifier_env *env)
{
bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
@@ -18415,6 +20508,12 @@ static int do_check(struct bpf_verifier_env *env)
struct bpf_insn_aux_data *insn_aux;
int err;
+ if (signal_pending(current))
+ return -EAGAIN;
+
+ if (need_resched())
+ cond_resched();
+
/* reset current history entry on each new instruction */
env->cur_hist_ent = NULL;
@@ -18461,6 +20560,15 @@ static int do_check(struct bpf_verifier_env *env)
}
}
+ /*
+ * Possibly widen the registers before creating a checkpoint
+ * in bpf_is_state_visited(). The next loop iteration will
+ * have a chance to hit this checkpoint and converge.
+ */
+ err = handle_loop_entry_exit(env);
+ if (err)
+ return err;
+
if (bpf_is_prune_point(env, env->insn_idx)) {
err = bpf_is_state_visited(env, env->insn_idx);
if (err < 0)
@@ -18481,17 +20589,11 @@ static int do_check(struct bpf_verifier_env *env)
}
if (bpf_is_jmp_point(env, env->insn_idx)) {
- err = bpf_push_jmp_history(env, state, 0, 0, 0, 0);
+ err = bpf_push_jmp_history(env, state, 0, 0, 0, NULL, 0);
if (err)
return err;
}
- if (signal_pending(current))
- return -EAGAIN;
-
- if (need_resched())
- cond_resched();
-
if (env->log.level & BPF_LOG_LEVEL2 && do_print_state) {
verbose(env, "\nfrom %d to %d%s:",
env->prev_insn_idx, env->insn_idx,
@@ -18501,6 +20603,13 @@ static int do_check(struct bpf_verifier_env *env)
do_print_state = false;
}
+ if (bpf_loop_at_index(env, env->insn_idx) >= 0 &&
+ cur_func(env)->loop_stack_cnt == 0) {
+ verifier_bug(env, "loop stack empty at %d, while inside the loop %d\n",
+ env->insn_idx, bpf_loop_at_index(env, env->insn_idx));
+ return -EFAULT;
+ }
+
if (env->log.level & BPF_LOG_LEVEL) {
if (verifier_state_scratched(env))
print_insn_state(env, state, state->curframe);
@@ -18833,12 +20942,6 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
{
enum bpf_prog_type prog_type = resolve_prog_type(prog);
- if (map->excl_prog_sha &&
- memcmp(map->excl_prog_sha, prog->digest, SHA256_DIGEST_SIZE)) {
- verbose(env, "program's hash doesn't match map's excl_prog_hash\n");
- return -EACCES;
- }
-
if (btf_record_has_field(map->record, BPF_LIST_HEAD) ||
btf_record_has_field(map->record, BPF_RB_ROOT)) {
if (is_tracing_prog_type(prog_type)) {
@@ -18872,35 +20975,11 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
return -EINVAL;
}
- if (prog->sleepable)
- switch (map->map_type) {
- case BPF_MAP_TYPE_HASH:
- case BPF_MAP_TYPE_RHASH:
- case BPF_MAP_TYPE_LRU_HASH:
- case BPF_MAP_TYPE_ARRAY:
- case BPF_MAP_TYPE_PERCPU_HASH:
- case BPF_MAP_TYPE_PERCPU_ARRAY:
- case BPF_MAP_TYPE_LRU_PERCPU_HASH:
- case BPF_MAP_TYPE_LPM_TRIE:
- case BPF_MAP_TYPE_ARRAY_OF_MAPS:
- case BPF_MAP_TYPE_HASH_OF_MAPS:
- case BPF_MAP_TYPE_RINGBUF:
- case BPF_MAP_TYPE_USER_RINGBUF:
- case BPF_MAP_TYPE_INODE_STORAGE:
- case BPF_MAP_TYPE_SK_STORAGE:
- case BPF_MAP_TYPE_TASK_STORAGE:
- case BPF_MAP_TYPE_CGRP_STORAGE:
- case BPF_MAP_TYPE_QUEUE:
- case BPF_MAP_TYPE_STACK:
- case BPF_MAP_TYPE_ARENA:
- case BPF_MAP_TYPE_INSN_ARRAY:
- case BPF_MAP_TYPE_PROG_ARRAY:
- break;
- default:
- verbose(env,
- "Sleepable programs can only use array, hash, ringbuf and local storage maps\n");
- return -EINVAL;
- }
+ if (prog->sleepable && !map_needs_no_rcu(map)) {
+ verbose(env,
+ "Sleepable programs can only use array, hash, ringbuf and local storage maps\n");
+ return -EINVAL;
+ }
if (bpf_map_is_cgroup_storage(map) &&
bpf_cgroup_storage_assign(env->prog->aux, map)) {
@@ -18936,6 +21015,85 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
return 0;
}
+/*
+ * Keep track of whether the map is used by one program only. Such program may
+ * store the addresses of its functions into the map when it's frozen, see
+ * resolve_func_ptrs(), since nothing else relies on what the map has. After
+ * that the map is not available to other programs.
+ */
+static int bpf_map_claim(struct bpf_verifier_env *env, struct bpf_map *map)
+{
+ unsigned long me = (unsigned long)env->prog->aux, old;
+
+ for (;;) {
+ old = READ_ONCE(map->user);
+ if (old == me || old == BPF_MAP_USER_MANY)
+ return 0;
+ if (old & BPF_MAP_USER_PATCHED) {
+ verbose(env, "map '%s' has addresses of functions of another program\n",
+ map->name);
+ return -EBUSY;
+ }
+ if (cmpxchg(&map->user, old, old ? BPF_MAP_USER_MANY : me) == old)
+ return 0;
+ }
+}
+
+/*
+ * An exclusive map is bound to the digest of its program. The digest covers
+ * the insns as they are verified, after the CO-RE relocations the kernel
+ * applies, so it exists only once check_and_resolve_insns() has computed it,
+ * and the maps are checked against it there, whether an insn referred to
+ * them or a continuous fd_array bound them before.
+ */
+static int check_excl_maps(struct bpf_verifier_env *env)
+{
+ int i;
+
+ for (i = 0; i < env->used_map_cnt; i++) {
+ struct bpf_map *map = env->used_maps[i];
+
+ if (map->excl_prog_sha &&
+ memcmp(map->excl_prog_sha, env->prog->digest, SHA256_DIGEST_SIZE)) {
+ verbose(env, "program's hash doesn't match map's excl_prog_hash\n");
+ return -EACCES;
+ }
+ }
+ return 0;
+}
+
+/*
+ * The program may use every map it referred to, see check_excl_maps(). Only now
+ * does it leave a trace on them: it claims each as its user, see
+ * bpf_map_claim(), and takes over its insn arrays. A program rejected before
+ * this point, one that is not the owner of an exclusive map in particular,
+ * leaves the maps as it found them. Nothing consults a claim before
+ * resolve_func_ptrs() and the fixup passes, which run later.
+ */
+static int claim_used_maps(struct bpf_verifier_env *env)
+{
+ int i, err;
+
+ for (i = 0; i < env->used_map_cnt; i++) {
+ struct bpf_map *map = env->used_maps[i];
+
+ err = bpf_map_claim(env, map);
+ if (err)
+ return err;
+
+ if (map->map_type != BPF_MAP_TYPE_INSN_ARRAY)
+ continue;
+ err = bpf_insn_array_init(map, env->prog);
+ if (err) {
+ verbose(env, "Failed to properly initialize insn array\n");
+ return err;
+ }
+ env->insn_array_maps[env->insn_array_map_cnt++] = map;
+ env->prog->jit_required = true;
+ }
+ return 0;
+}
+
static int __add_used_map(struct bpf_verifier_env *env, struct bpf_map *map)
{
int i, err;
@@ -18961,7 +21119,7 @@ static int __add_used_map(struct bpf_verifier_env *env, struct bpf_map *map)
if (err)
return err;
- if (env->prog->sleepable)
+ if (bpf_prog_runs_sleepable(env->prog))
atomic64_inc(&map->sleepable_refcnt);
/* hold the map. If the program is rejected by verifier,
@@ -18973,16 +21131,6 @@ static int __add_used_map(struct bpf_verifier_env *env, struct bpf_map *map)
env->used_maps[env->used_map_cnt++] = map;
- if (map->map_type == BPF_MAP_TYPE_INSN_ARRAY) {
- err = bpf_insn_array_init(map, env->prog);
- if (err) {
- verbose(env, "Failed to properly initialize insn array\n");
- return err;
- }
- env->insn_array_maps[env->insn_array_map_cnt++] = map;
- env->prog->jit_required = true;
- }
-
return env->used_map_cnt - 1;
}
@@ -19127,6 +21275,14 @@ static int check_jmp_fields(struct bpf_verifier_env *env, struct bpf_insn *insn)
switch (opcode) {
case BPF_CALL:
+ if (bpf_is_callx(insn)) {
+ /* callx dst_reg */
+ if (insn->src_reg != BPF_REG_0 || insn->imm != 0 || insn->off != 0) {
+ verbose(env, "BPF_CALL|BPF_X uses reserved fields\n");
+ return -EINVAL;
+ }
+ return 0;
+ }
if (BPF_SRC(insn->code) != BPF_K ||
(insn->src_reg != BPF_PSEUDO_KFUNC_CALL && insn->off != 0) ||
(insn->src_reg != BPF_REG_0 && insn->src_reg != BPF_PSEUDO_CALL &&
@@ -19151,14 +21307,14 @@ static int check_jmp_fields(struct bpf_verifier_env *env, struct bpf_insn *insn)
return 0;
case BPF_EXIT:
if (BPF_SRC(insn->code) != BPF_K || insn->imm != 0 ||
- insn->src_reg != BPF_REG_0 || insn->dst_reg != BPF_REG_0 ||
+ insn->src_reg > BPF_UNWIND_EXIT || insn->dst_reg != BPF_REG_0 ||
class == BPF_JMP32) {
verbose(env, "BPF_EXIT uses reserved fields\n");
return -EINVAL;
}
return 0;
case BPF_JCOND:
- if (insn->code != (BPF_JMP | BPF_JCOND) || insn->src_reg != BPF_MAY_GOTO ||
+ if (insn->code != (BPF_JMP | BPF_JCOND) || insn->src_reg > BPF_UNWIND_GOTO ||
insn->dst_reg || insn->imm) {
verbose(env, "invalid may_goto imm %d\n", insn->imm);
return -EINVAL;
@@ -19378,6 +21534,107 @@ next_insn:
* 'struct bpf_map *' into a register instead of user map_fd.
* These pointers will be used later by verifier to validate map access.
*/
+ err = check_excl_maps(env);
+ if (err)
+ return err;
+ return claim_used_maps(env);
+}
+
+/* that many pointers to functions are recognized in the maps of a program at most */
+#define BPF_MAX_FUNC_PTRS (1 << 20)
+
+static int add_func_ptr(struct bpf_verifier_env *env, struct bpf_map *map, u32 map_off,
+ u32 xlated_off)
+{
+ struct bpf_func_ptr *ptrs;
+
+ if (env->func_ptr_cnt == BPF_MAX_FUNC_PTRS) {
+ verbose(env, "too many pointers to functions in maps, the limit is %d\n",
+ BPF_MAX_FUNC_PTRS);
+ return -E2BIG;
+ }
+
+ /* grow by doubling, the array is sorted and searched later */
+ if (!(env->func_ptr_cnt & (env->func_ptr_cnt - 1))) {
+ ptrs = kvrealloc(env->func_ptrs,
+ array_size(max(2 * env->func_ptr_cnt, 16U), sizeof(*ptrs)),
+ GFP_KERNEL_ACCOUNT);
+ if (!ptrs)
+ return -ENOMEM;
+ env->func_ptrs = ptrs;
+ }
+ env->func_ptrs[env->func_ptr_cnt++] = (struct bpf_func_ptr){
+ .map = map,
+ .map_off = map_off,
+ .orig_off = xlated_off,
+ .xlated_off = xlated_off,
+ };
+ return 0;
+}
+
+/*
+ * Compilers put pointers to functions into read-only data: tables of functions,
+ * structures of operations, vtables, where they are mixed with other data.
+ * The loader stores such data in a frozen read-only array map and resolves
+ * a pointer to a static function to the offset in bytes of its first
+ * instruction in the program: the address of the function in the program.
+ *
+ * Find 64-bit values that look like that in the maps of a program that uses
+ * callx. It's a guess. When the value is not a pointer, the program either
+ * fails to load, because it does with a pointer what can be done with
+ * a number only, or it sees the address of a function instead of the number.
+ * It's known before the control flow graph of the program is built and the main
+ * verification pass begins which functions may be called via callx.
+ *
+ * When the program is jitted the offsets are replaced with the addresses of
+ * the functions in the map itself, see jit_subprogs(). Hence the program has to
+ * be the only user of the map, see bpf_map_claim(): nothing else may rely on
+ * what the map had.
+ *
+ * The program reads the addresses of its functions from there like any other
+ * data, so it has to be allowed to leak pointers.
+ */
+static int resolve_func_ptrs(struct bpf_verifier_env *env)
+{
+ int insn_cnt = env->prog->len;
+ int i, err, subprog;
+ struct bpf_map *map;
+ u64 addr, val;
+ u32 off;
+
+ if (!env->has_callx || !env->allow_ptr_leaks)
+ return 0;
+
+ for (i = 0; i < env->used_map_cnt; i++) {
+ map = env->used_maps[i];
+ /* coincidences in maps that are shared with other programs don't matter */
+ if (READ_ONCE(map->user) != (unsigned long)env->prog->aux)
+ continue;
+ if (map->map_type != BPF_MAP_TYPE_ARRAY || map->max_entries != 1 ||
+ !bpf_map_is_rdonly(map) || !map->ops->map_direct_value_addr ||
+ !IS_ERR_OR_NULL(map->record))
+ continue;
+ if (map->ops->map_direct_value_addr(map, &addr, 0))
+ continue;
+
+ for (off = 0; off + sizeof(u64) <= map->value_size; off += sizeof(u64)) {
+ if (!(off % SZ_64K))
+ cond_resched();
+ val = *(u64 *)(unsigned long)(addr + off);
+ if (!val || val % sizeof(struct bpf_insn) ||
+ val / sizeof(struct bpf_insn) >= insn_cnt)
+ continue;
+ subprog = bpf_find_subprog(env, val / sizeof(struct bpf_insn));
+ if (subprog <= 0 || bpf_subprog_is_global(env, subprog))
+ continue;
+ err = add_func_ptr(env, map, off, val / sizeof(struct bpf_insn));
+ if (err)
+ return err;
+ }
+ }
+ if (env->func_ptr_cnt)
+ sort(env->func_ptrs, env->func_ptr_cnt, sizeof(*env->func_ptrs),
+ cmp_func_ptrs, NULL);
return 0;
}
@@ -19488,7 +21745,8 @@ static void free_states(struct bpf_verifier_env *env)
}
}
-static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sleepable)
+static int do_check_common(struct bpf_verifier_env *env, int subprog,
+ enum bpf_func_context context)
{
bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
struct bpf_subprog_info *sub = subprog_info(env, subprog);
@@ -19508,7 +21766,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sl
state->curframe = 0;
state->speculative = false;
state->branches = 1;
- state->in_sleepable = is_sleepable;
+ state->in_sleepable = context == BPF_FUNC_CTX_SLEEPABLE;
+ env->no_rcu_read_lock = context == BPF_FUNC_CTX_NO_RCU_LOCK;
state->frame[0] = kzalloc_obj(struct bpf_func_state, GFP_KERNEL_ACCOUNT);
if (!state->frame[0]) {
kfree(state);
@@ -19548,25 +21807,30 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sl
}
/* Also ensure the callback only has a single scalar argument. */
- if (sub->arg_cnt != 1 || sub->args[0].arg_type != ARG_ANYTHING) {
+ if (sub->arg_slot_cnt != 1 || sub->args[0].arg_type != ARG_SCALAR) {
verbose(env, "exception cb only supports single integer argument\n");
ret = -EINVAL;
goto out;
}
}
- for (i = BPF_REG_1; i <= min_t(u32, sub->arg_cnt, MAX_BPF_FUNC_REG_ARGS); i++) {
+ for (i = BPF_REG_1;
+ i <= min_t(u32, sub->arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS); i++) {
arg = &sub->args[i - BPF_REG_1];
reg = &regs[i];
if (arg->arg_type == ARG_PTR_TO_CTX) {
reg->type = PTR_TO_CTX;
mark_reg_known_zero(env, regs, i);
- } else if (arg->arg_type == ARG_ANYTHING) {
+ } else if (arg->arg_type == ARG_SCALAR) {
reg->type = SCALAR_VALUE;
mark_reg_unknown(env, regs, i);
} else if (arg->arg_type == ARG_PTR_TO_DYNPTR) {
- /* assume unspecial LOCAL dynptr type */
- __mark_dynptr_reg(reg, BPF_DYNPTR_TYPE_LOCAL, true, ++env->id_gen, 0);
+ /*
+ * The callers may hand in a dynptr of any type.
+ * Take it for that of an skb, so that a write
+ * through it and its slices are treated with care.
+ */
+ __mark_dynptr_reg(reg, BPF_DYNPTR_TYPE_SKB, true, ++env->id_gen, 0);
} else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) {
reg->type = PTR_TO_MEM;
reg->type |= arg->arg_type &
@@ -19597,7 +21861,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sl
goto out;
}
}
- if (env->prog->type == BPF_PROG_TYPE_EXT && sub->arg_cnt > MAX_BPF_FUNC_REG_ARGS) {
+ if (env->prog->type == BPF_PROG_TYPE_EXT &&
+ sub->arg_slot_cnt > MAX_BPF_FUNC_REG_ARGS) {
verbose(env, "freplace programs with >%d args not supported yet\n",
MAX_BPF_FUNC_REG_ARGS);
ret = -EINVAL;
@@ -19610,9 +21875,10 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sl
*/
if (env->prog->aux->func_info_aux) {
ret = btf_prepare_func_args(env, 0);
- if (ret || sub->arg_cnt != 1 || sub->args[0].arg_type != ARG_PTR_TO_CTX) {
+ if (ret || sub->arg_slot_cnt != 1 ||
+ sub->args[0].arg_type != ARG_PTR_TO_CTX) {
env->prog->aux->func_info_aux[0].unreliable = true;
- sub->arg_cnt = 1;
+ sub->arg_slot_cnt = 1;
sub->stack_arg_cnt = 0;
}
}
@@ -19722,7 +21988,7 @@ again:
/*
* We can't loop forever as each pass verifies at least one new context,
- * and there are only two contexts per global subprog.
+ * and there are only three contexts per global subprog.
*/
if (new_cnt)
goto again;
@@ -20108,6 +22374,109 @@ static bool attach_uses_trampoline_retval(enum bpf_attach_type type)
}
}
+/*
+ * BPF code passes argument slot N where the kernel convention passes its Nth
+ * u64 argument, so describe a BPF function to its trampoline as one u64 per
+ * slot, and every arch places its arguments right.
+ */
+static int flatten_bpf_func_model(struct bpf_verifier_log *log, const char *tname,
+ struct btf_func_model *m)
+{
+ u32 i, nslots = 0;
+
+ for (i = 0; i < m->nr_args; i++)
+ nslots += btf_func_model_arg_slots(m, i);
+ if (nslots > MAX_BPF_FUNC_ARGS) {
+ bpf_log(log, "The function %s has %u argument slots. Too many.\n",
+ tname, nslots);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nslots; i++) {
+ m->arg_size[i] = sizeof(u64);
+ m->arg_flags[i] = 0;
+ }
+ m->nr_args = nslots;
+ return 0;
+}
+
+/*
+ * The verifier lets a static function use stack arguments counted from its
+ * BTF, even if that BTF turns out unreliable. A trampoline on the function is
+ * then given only MAX_BPF_FUNC_REG_ARGS u64 arguments and would lose the
+ * stack arguments, so such a function cannot be attached to.
+ */
+static int check_unreliable_func_args(struct bpf_verifier_log *log, struct btf *btf,
+ const struct btf_type *func_proto, const char *tname)
+{
+ struct btf_func_model m;
+ u32 i, nslots = 0;
+
+ if (btf_distill_func_proto(NULL, btf, func_proto, tname, &m)) {
+ /* at least a slot for each argument */
+ nslots = btf_type_vlen(func_proto);
+ } else {
+ for (i = 0; i < m.nr_args; i++)
+ nslots += btf_func_model_arg_slots(&m, i);
+ }
+ if (nslots <= MAX_BPF_FUNC_REG_ARGS)
+ return 0;
+
+ bpf_log(log, "Cannot attach to static function %s: unreliable BTF and stack arguments\n",
+ tname);
+ return -EINVAL;
+}
+
+/* Functions may share a type id. The first of them is attached to. */
+static int find_tgt_subprog(const struct bpf_prog *tgt_prog, u32 btf_id)
+{
+ const struct bpf_prog_aux *aux = tgt_prog->aux;
+ int i;
+
+ for (i = 0; i < aux->func_info_cnt; i++)
+ if (aux->func_info[i].type_id == btf_id)
+ return i;
+
+ return -1;
+}
+
+/*
+ * The prologue of an extension gives a cloned skb a new head, unless the
+ * prologue of the target program has done that. The callers of the function
+ * that it replaces must not hold on to the old head.
+ */
+int bpf_check_ext_prologue(struct bpf_verifier_log *log,
+ const struct bpf_prog *tgt_prog, u32 btf_id)
+{
+ const struct bpf_prog_aux *aux = tgt_prog->aux;
+ int subprog = find_tgt_subprog(tgt_prog, btf_id);
+
+ if (aux->has_prologue)
+ return 0;
+
+ if (subprog >= 0 && !aux->func_info_aux[subprog].called_with_pkt_ptrs)
+ return 0;
+
+ bpf_log(log,
+ "Extension writing packet data cannot replace a function whose callers keep packet pointers\n");
+ return -EINVAL;
+}
+
+/*
+ * An extension is verified as a program that runs under the RCU read lock. Does
+ * the function that it is to replace?
+ */
+static bool func_has_rcu_read_lock(const struct bpf_prog *prog, int subprog)
+{
+ const struct bpf_func_info_aux *sub_aux = &prog->aux->func_info_aux[subprog];
+
+ if (!subprog)
+ return !prog->sleepable;
+
+ return !sub_aux->called[BPF_FUNC_CTX_SLEEPABLE] &&
+ !sub_aux->called[BPF_FUNC_CTX_NO_RCU_LOCK];
+}
+
int bpf_check_attach_target(struct bpf_verifier_log *log,
const struct bpf_prog *prog,
const struct bpf_prog *tgt_prog,
@@ -20119,7 +22488,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
char trace_symbol[KSYM_SYMBOL_LEN];
const char prefix[] = "btf_trace_";
struct bpf_raw_event_map *btp;
- int ret = 0, subprog = -1, i;
+ int ret = 0, subprog = -1;
const struct btf_type *t;
bool conservative = true;
const char *tname, *fname;
@@ -20151,6 +22520,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
struct bpf_prog_aux *aux = tgt_prog->aux;
bool tgt_changes_pkt_data;
bool tgt_might_sleep;
+ bool tgt_can_unwind;
if (bpf_prog_is_dev_bound(prog->aux) &&
!bpf_prog_dev_bound_match(prog, tgt_prog)) {
@@ -20158,11 +22528,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
return -EINVAL;
}
- for (i = 0; i < aux->func_info_cnt; i++)
- if (aux->func_info[i].type_id == btf_id) {
- subprog = i;
- break;
- }
+ subprog = find_tgt_subprog(tgt_prog, btf_id);
if (subprog == -1) {
bpf_log(log, "Subprog %s doesn't exist\n", tname);
return -EINVAL;
@@ -20183,9 +22549,20 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
prog_extension ? "Extension" : "Tracing");
return -EINVAL;
}
+ /* unwind_exit of the main prog is exit, unless it's an extension */
+ tgt_can_unwind = (subprog || tgt_prog->type == BPF_PROG_TYPE_EXT) &&
+ (aux->func ? aux->func[subprog]->aux->can_unwind
+ : aux->can_unwind);
+ /* unwind_exit would return to the trampoline that called the target */
+ if (tgt_can_unwind && !prog_extension &&
+ prog->expected_attach_type != BPF_TRACE_FENTRY) {
+ bpf_log(log, "Only fentry can attach to %s, which can unwind\n", tname);
+ return -EINVAL;
+ }
conservative = aux->func_info_aux[subprog].unreliable;
if (prog_extension) {
- if (conservative) {
+ if (aux->func_info_aux[subprog].linkage != BTF_FUNC_GLOBAL ||
+ conservative) {
bpf_log(log,
"Cannot replace static functions\n");
return -EINVAL;
@@ -20195,6 +22572,20 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
"Extension programs should be JITed\n");
return -EINVAL;
}
+ /*
+ * How much of the buffer of the tracepoint is used
+ * is checked for the program that is attached to it.
+ */
+ if (tgt_prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
+ bpf_log(log,
+ "Cannot extend a writable raw tracepoint program\n");
+ return -EINVAL;
+ }
+ if (!func_has_rcu_read_lock(tgt_prog, subprog)) {
+ bpf_log(log,
+ "Cannot extend a function that runs without RCU read lock\n");
+ return -EINVAL;
+ }
tgt_changes_pkt_data = aux->func
? aux->func[subprog]->aux->changes_pkt_data
: aux->changes_pkt_data;
@@ -20203,6 +22594,9 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
"Extension program changes packet data, while original does not\n");
return -EINVAL;
}
+ if (prog->aux->has_prologue &&
+ bpf_check_ext_prologue(log, tgt_prog, btf_id))
+ return -EINVAL;
tgt_might_sleep = aux->func
? aux->func[subprog]->aux->might_sleep
@@ -20212,6 +22606,12 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
"Extension program may sleep, while original does not\n");
return -EINVAL;
}
+ /* There is no unwind_goto after the calls of the original */
+ if (prog->aux->can_unwind && !tgt_can_unwind) {
+ bpf_log(log,
+ "Extension program can unwind, while original does not\n");
+ return -EINVAL;
+ }
}
if (!tgt_prog->jited) {
bpf_log(log, "Can attach to only JITed progs\n");
@@ -20375,12 +22775,23 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
prog->aux->saved_dst_attach_type != tgt_prog->expected_attach_type))
return -EINVAL;
- if (tgt_prog && conservative)
+ if (tgt_prog && conservative) {
+ if (subprog > 0) {
+ ret = check_unreliable_func_args(log, btf, t, tname);
+ if (ret)
+ return ret;
+ }
t = NULL;
+ }
ret = btf_distill_func_proto(log, btf, t, tname, &tgt_info->fmodel);
if (ret < 0)
return ret;
+ if (tgt_prog) {
+ ret = flatten_bpf_func_model(log, tname, &tgt_info->fmodel);
+ if (ret < 0)
+ return ret;
+ }
if (tgt_info->fmodel.ret_size > 8 &&
attach_uses_trampoline_retval(prog->expected_attach_type)) {
@@ -20857,6 +23268,66 @@ static int specialize_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_desc
return 0;
}
+static int add_kfunc_desc_target(struct bpf_verifier_env *env,
+ const struct bpf_kfunc_desc *target_desc,
+ u16 base_desc_idx, u16 *desc_idx)
+{
+ struct bpf_kfunc_desc desc = *target_desc;
+ struct bpf_kfunc_desc_tab *new_tab;
+ struct bpf_kfunc_desc_tab *tab;
+ struct bpf_prog_aux *prog_aux;
+ u32 i;
+
+ prog_aux = env->prog->aux;
+ tab = prog_aux->kfunc_tab;
+
+ if (base_desc_idx >= tab->nr_base_descs) {
+ verifier_bug(env, "kfunc desc_idx %d out of bounds (%d descriptors)",
+ base_desc_idx, tab->nr_base_descs);
+ return -EFAULT;
+ }
+ if (tab->descs[base_desc_idx].func_id != desc.func_id) {
+ verifier_bug(env, "kfunc desc %d has invalid id (expected %d, got %d)",
+ base_desc_idx, tab->descs[base_desc_idx].func_id, desc.func_id);
+ return -EFAULT;
+ }
+ if (tab->descs[base_desc_idx].offset != desc.offset) {
+ verifier_bug(env, "kfunc desc %d has invalid offset (expected %d, got %d)",
+ base_desc_idx, tab->descs[base_desc_idx].offset, desc.offset);
+ return -EFAULT;
+ }
+
+ if (tab->descs[base_desc_idx].addr == desc.addr) {
+ *desc_idx = base_desc_idx;
+ return 0;
+ }
+
+ for (i = tab->nr_base_descs; i < tab->nr_descs; i++) {
+ if (tab->descs[i].func_id == desc.func_id &&
+ tab->descs[i].offset == desc.offset &&
+ tab->descs[i].addr == desc.addr) {
+ *desc_idx = i;
+ return 0;
+ }
+ }
+
+ if (tab->nr_descs == MAX_KFUNC_CALL_DESCS) {
+ verbose(env, "too many different kernel function call targets\n");
+ return -E2BIG;
+ }
+
+ new_tab = krealloc(tab, struct_size(tab, descs, tab->nr_descs + 1),
+ GFP_KERNEL_ACCOUNT);
+ if (!new_tab)
+ return -ENOMEM;
+ tab = new_tab;
+ prog_aux->kfunc_tab = tab;
+
+ *desc_idx = tab->nr_descs;
+ tab->descs[tab->nr_descs++] = desc;
+ return 0;
+}
+
static void __fixup_collection_insert_kfunc(struct bpf_insn_aux_data *insn_aux,
u16 struct_meta_reg,
u16 node_offset_reg,
@@ -20877,7 +23348,11 @@ static void __fixup_collection_insert_kfunc(struct bpf_insn_aux_data *insn_aux,
int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
struct bpf_insn *insn_buf, int insn_idx, int *cnt)
{
+ struct bpf_kfunc_desc desc_copy;
struct bpf_kfunc_desc *desc;
+ unsigned long call_imm;
+ u16 base_desc_idx, desc_idx;
+ bool near_call;
int err;
if (!insn->imm) {
@@ -20897,13 +23372,30 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
insn->imm);
return -EFAULT;
}
+ base_desc_idx = desc - env->prog->aux->kfunc_tab->descs;
+
+ near_call = !bpf_jit_supports_far_kfunc_call();
+ desc_copy = *desc;
+ desc = &desc_copy;
err = specialize_kfunc(env, desc, insn_idx);
if (err)
return err;
- if (!bpf_jit_supports_far_kfunc_call())
- insn->imm = BPF_CALL_IMM(desc->addr);
+ if (near_call) {
+ call_imm = BPF_CALL_IMM(desc->addr);
+ if ((unsigned long)(s32)call_imm != call_imm) {
+ verbose(env, "address of kernel func_id %u is out of range\n",
+ desc->func_id);
+ return -EINVAL;
+ }
+ insn->imm = call_imm;
+ }
+
+ err = add_kfunc_desc_target(env, desc, base_desc_idx, &desc_idx);
+ if (err)
+ return err;
+ insn->off = desc_idx;
if (is_bpf_obj_new_kfunc(desc->func_id) || is_bpf_percpu_obj_new_kfunc(desc->func_id)) {
struct btf_struct_meta *kptr_struct_meta = env->insn_aux_data[insn_idx].kptr_struct_meta;
@@ -21063,6 +23555,14 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return 0;
}
+/*
+ * Upper bound on the PKCS#7 signature blob passed with a program. Comfortably
+ * above the largest signature the kernel can verify, and far below anything
+ * that would make rejecting a load expensive. Deliberately a fixed number so
+ * that what the syscall accepts does not depend on PAGE_SIZE.
+ */
+#define BPF_PROG_MAX_SIGNATURE_SIZE (64 * 1024)
+
static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
{
switch (keyring_id) {
@@ -21072,6 +23572,8 @@ static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
return BPF_SIG_KEYRING_SECONDARY;
case (s32)(unsigned long)VERIFY_USE_PLATFORM_KEYRING:
return BPF_SIG_KEYRING_PLATFORM;
+ case KEY_SPEC_BPF_KEYRING:
+ return BPF_SIG_KEYRING_BPF;
default:
return BPF_SIG_KEYRING_USER;
}
@@ -21101,17 +23603,33 @@ static int bpf_prog_verify_signature(struct bpf_verifier_env *env,
int err = 0;
/*
- * Don't attempt to use kmalloc_large or vmalloc for signatures.
- * Practical signature for BPF program should be below this limit.
+ * The signature covers the insns as submitted. CO-RE relocations would
+ * have the kernel rewrite them afterwards, with values the loader picks
+ * through the BTF it supplies, see bpf_check_core_relo().
*/
+ if (attr->core_relo_cnt) {
+ verbose(env, "signed program cannot carry CO-RE relocations\n");
+ return -EACCES;
+ }
if (!attr->signature_size ||
- attr->signature_size > KMALLOC_MAX_CACHE_SIZE)
+ attr->signature_size > BPF_PROG_MAX_SIGNATURE_SIZE)
return -EINVAL;
- if (system_keyring_id_check(attr->keyring_id) == 0)
+
+ if (system_keyring_id_check(attr->keyring_id) == 0) {
key = bpf_lookup_system_key(attr->keyring_id);
- else
+ } else if (attr->keyring_id == KEY_SPEC_BPF_KEYRING) {
+ key = bpf_lookup_keyring();
+ } else if (bpf_keyring_enforced()) {
+ verbose(env, "caller-supplied keyring refused, use bpf keyring\n");
+ return -EPERM;
+ } else {
key = bpf_lookup_user_key(attr->keyring_id, 0);
+ }
if (!key) {
+ if (attr->keyring_id == KEY_SPEC_BPF_KEYRING) {
+ verbose(env, "the bpf keyring is empty or has not been restricted\n");
+ return -ENOKEY;
+ }
verbose(env, "cannot resolve signing keyring with keyring_id %d\n",
attr->keyring_id);
return -EINVAL;
@@ -21203,6 +23721,65 @@ out:
return err;
}
+/*
+ * bpf_clone_redirect() transmits from inside the program, so a tc egress or
+ * lwt_xmit program that redirects to its own device runs again on top of its
+ * own frame, and again from there, until the datapath's recursion limit drops
+ * the packet: ten frames deep. Ten MAX_BPF_STACK frames fit the kernel stack
+ * as they always did; ten MAX_BPF_STACK_JIT frames would not, so a program
+ * calling it keeps the smaller budget. The redirect helpers that transmit
+ * after the program has returned leave no frame behind.
+ */
+static bool bpf_prog_reenters_datapath(const struct bpf_prog *prog)
+{
+ const struct bpf_insn *insn = prog->insnsi;
+ int i;
+
+ for (i = 0; i < prog->len; i++, insn++)
+ if (bpf_helper_call(insn) && insn->imm == BPF_FUNC_clone_redirect)
+ return true;
+ return false;
+}
+
+/* Various log level 2 information about the program */
+static void log_program(struct bpf_verifier_env *env)
+{
+ struct bpf_insn_aux_data *insn_aux = env->insn_aux_data;
+ struct bpf_insn *insns = env->prog->insnsi;
+ u32 insn_cnt = env->prog->len;
+ u64 pos, insn_pos;
+ u32 i, j;
+
+ verbose(env, "Program dump (scc? loop_header? idom insn#: live_regs_before):\n");
+ for (i = 0; i < insn_cnt; ++i) {
+ verbose_linfo(env, i, " ; ");
+ if (env->insn_aux_data[i].scc)
+ verbose(env, "%3d ", env->insn_aux_data[i].scc);
+ else
+ verbose(env, " ");
+ if (env->insn_aux_data[i].loop_header >= 0)
+ verbose(env, "%3d ", env->insn_aux_data[i].loop_header);
+ else
+ verbose(env, " ");
+ verbose(env, "%3d ", env->idoms[i]);
+ verbose(env, "%3d: ", i);
+ for (j = BPF_REG_0; j < BPF_REG_10; ++j)
+ if (insn_aux[i].live_regs_before & BIT(j))
+ verbose(env, "%d", j);
+ else
+ verbose(env, ".");
+ verbose(env, " ");
+ pos = env->log.end_pos;
+ bpf_verbose_insn(env, &insns[i]);
+ insn_pos = env->log.end_pos;
+ if (insn_aux[i].zext_dst)
+ verbose(env, "%*c; zext", bpf_vlog_alignment(insn_pos - pos), ' ');
+ verbose(env, "\n");
+ if (bpf_is_ldimm64(&insns[i]))
+ i++;
+ }
+}
+
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
struct bpf_log_attr *attr_log)
{
@@ -21227,6 +23804,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
env->bt.env = env;
env->prog = *prog;
env->ops = bpf_verifier_ops[env->prog->type];
+ env->stack_limit = bpf_prog_stack_limit(env->prog);
+ if (bpf_prog_reenters_datapath(env->prog))
+ env->stack_limit = MAX_BPF_STACK;
env->allow_ptr_leaks = bpf_allow_ptr_leaks(env->prog->aux->token);
env->allow_uninit_stack = bpf_allow_uninit_stack(env->prog->aux->token);
@@ -21249,11 +23829,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
ret = -EINVAL;
goto err_prep;
}
- if (env->signature) {
- ret = bpf_prog_calc_tag(env->prog);
- if (ret < 0)
- goto err_prep;
- }
ret = process_fd_array(env, attr, uattr);
if (ret)
@@ -21307,6 +23882,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (is_priv)
env->test_state_freq = attr->prog_flags & BPF_F_TEST_STATE_FREQ;
env->test_reg_invariants = attr->prog_flags & BPF_F_TEST_REG_INVARIANTS;
+ env->arena_scalar = attr->prog_flags & BPF_F_ARENA_SCALAR;
env->explored_states = kvzalloc_objs(struct list_head,
state_htab_size(env),
@@ -21329,17 +23905,19 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (ret < 0)
goto skip_full_check;
- /* Discover all subprograms before validating their layout and BTF. */
+ /* Discover all subprograms and collect the properties needed by BTF validation. */
ret = add_subprogs(env);
if (ret < 0)
goto skip_full_check;
- ret = check_subprogs(env);
+ find_subprog_properties(env);
+
+ /* Validate BTF before reporting subprogram layout errors. */
+ ret = bpf_check_btf_info(env, attr, uattr);
if (ret < 0)
goto skip_full_check;
- /* Validate BTF against the complete subprogram layout. */
- ret = bpf_check_btf_info(env, attr, uattr);
+ ret = check_subprogs(env);
if (ret < 0)
goto skip_full_check;
@@ -21348,6 +23926,11 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (ret < 0)
goto skip_full_check;
+ /* Find pointers to functions in the read-only maps of the program. */
+ ret = resolve_func_ptrs(env);
+ if (ret < 0)
+ goto skip_full_check;
+
/* Build kfunc prototypes after resolving program resources. */
ret = add_kfuncs(env);
if (ret < 0)
@@ -21363,6 +23946,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (ret < 0)
goto skip_full_check;
+ ret = check_unwind_insns(env);
+ if (ret < 0)
+ goto skip_full_check;
+
ret = bpf_compute_postorder(env);
if (ret < 0)
goto skip_full_check;
@@ -21391,10 +23978,38 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (ret < 0)
goto skip_full_check;
+ /* must precede the first bpf_ret_reg_pair() user below */
+ ret = bpf_compute_subprog_ret_regs(env);
+ if (ret < 0)
+ goto skip_full_check;
+
+ prepare_subprog_args(env);
+
+ ret = bpf_compute_idoms(env);
+ if (ret < 0)
+ goto skip_full_check;
+
+ ret = bpf_compute_loops(env);
+ if (ret < 0)
+ goto skip_full_check;
+
ret = bpf_compute_live_registers(env);
if (ret < 0)
goto skip_full_check;
+ if (env->log.level & BPF_LOG_LEVEL2)
+ log_program(env);
+
+ ret = bpf_init_scev(env);
+ if (ret < 0)
+ goto skip_full_check;
+
+ ret = bpf_compute_scev(env);
+ if (ret < 0)
+ goto skip_full_check;
+
+ bpf_free_subprog_jts(env);
+
ret = mark_fastcall_patterns(env);
if (ret < 0)
goto skip_full_check;
@@ -21402,6 +24017,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
ret = do_check_main(env);
ret = ret ?: do_check_subprogs(env);
+ /* reject recursion through the callx edges found by the main pass */
+ if (ret == 0 && env->callx_edges)
+ ret = sort_subprogs_topo(env);
+
if (ret == 0 && bpf_prog_is_offloaded(env->prog->aux))
ret = bpf_prog_offload_finalize(env);
@@ -21535,16 +24154,22 @@ err_prep:
release_maps(env);
release_btfs(env);
err_free_env:
+ bpf_free_subprog_jts(env);
if (env->insn_aux_data)
bpf_clear_insn_aux_data(env, 0, env->insn_aux_data_len);
vfree(env->insn_aux_data);
kvfree(env->fd_array);
+ bpf_free_scev(env);
bpf_stack_liveness_free(env);
+ kvfree(env->cfg.postorder_nums);
kvfree(env->cfg.insn_postorder);
kvfree(env->scc_info);
kvfree(env->succ);
kvfree(env->gotox_tmp_buf);
+ kvfree(env->callx_edges);
+ kvfree(env->func_ptrs);
bpf_diag_free(env);
+ kvfree(env->idoms);
kvfree(env);
return ret;
}