From: Alexei Starovoitov If map_extra is 0, the first arena_map_mmap() sets user_vm_start and user_vm_end. It holds arena->lock, but bpf_arena_free_pages() reads both fields without this mutex. The kfunc relies on the verifier, which rejects a prog if user_vm_start of its arena is 0. But this check covers only the maps in used_maps. The kfunc accepts any trusted pointer to struct bpf_map. Thus the kfunc can run during arena_map_mmap() and read user_vm_start as 0 and user_vm_end as vma->vm_end. Then min() does not limit page_cnt to the arena. arena_free_range() unmaps and frees pages of other vmalloc areas. Read user_vm_start one time and return if it is 0. This value means that no range is set, because arena_get_unmapped_area() rejects address 0. If only user_vm_end is still 0, uaddr_end is 0 and the kfunc returns, as today. Fixes: 317460317a02 ("bpf: Introduce bpf_arena.") Signed-off-by: Alexei Starovoitov --- kernel/bpf/arena.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index ad8aab551093..e331130662be 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -991,6 +991,8 @@ static int arena_free_range(struct bpf_arena *arena, long uaddr, long page_cnt) static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, bool sleepable) { + /* The first mmap() may be setting the user range right now */ + u64 user_vm_start = READ_ONCE(arena->user_vm_start); struct mem_cgroup *new_memcg, *old_memcg; u64 full_uaddr, uaddr_end; struct arena_free_span *s; @@ -998,8 +1000,8 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, /* only aligned lower 32-bit are relevant */ uaddr = (u32)uaddr; uaddr &= PAGE_MASK; - full_uaddr = clear_lo32(arena->user_vm_start) + uaddr; - if (full_uaddr < arena->user_vm_start) + full_uaddr = clear_lo32(user_vm_start) + uaddr; + if (!user_vm_start || full_uaddr < user_vm_start) return; uaddr_end = min(arena->user_vm_end, full_uaddr + (page_cnt << PAGE_SHIFT)); if (full_uaddr >= uaddr_end) -- 2.55.0