> diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
> index 7b6847200b43..fa462a0ff1ad 100644
> --- a/kernel/bpf/arena.c
> +++ b/kernel/bpf/arena.c
[ ... ]
> @@ -489,59 +490,106 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
> kbase = bpf_arena_get_kern_vm_start(arena);
> kaddr = kbase + (u32)(vmf->address);
>
> + page = vmalloc_to_page((void *)kaddr);
> + if (!page && !(arena->map.map_flags & BPF_F_SEGV_ON_FAULT)) {
> + /*
> + * Preallocate outside the lock with a sleepable allocator so it
> + * can reclaim and run the memcg OOM killer, which the
> + * non-blocking allocator under arena->spinlock cannot. A NULL
> + * return is non-recoverable, so fail with VM_FAULT_SIGBUS;
> + * VM_FAULT_OOM would be retried by the fault path and can
> + * livelock when the charged memcg is not the faulting task's.
> + */
> + bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
> + new_page = bpf_map_alloc_page_sleepable(map);
> + bpf_map_memcg_exit(old_memcg, new_memcg);
> + if (!new_page)
> + return VM_FAULT_SIGBUS;
> + }
> +
> + if (raw_res_spin_lock_irqsave(&arena->spinlock, flags)) {
[ ... ]
> page = vmalloc_to_page((void *)kaddr);
> if (page) {
> + if (page == arena->scratch_page) {
> + /*
> + * A scratch page marks a hole. Segfault only if the
> user
> + * asked for it; otherwise we could lazy-allocate but
> + * choose not to over a hole, so report a bus error.
> + */
> + fault_ret = (arena->map.map_flags &
> BPF_F_SEGV_ON_FAULT) ?
> + VM_FAULT_SIGSEGV : VM_FAULT_SIGBUS;
> + goto out_err_locked;
> + }
This isn't a bug, but could this comment spell out why a scratch-marked
hole is not lazy-allocated even though an untouched hole is? A reader might
wonder why the two cases are treated differently since both are equally
free as far as the range tree is concerned.
> /* 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;
> + }
> +
> bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
>
> + if (!new_page) {
> + /*
> + * Very rare race: the bpf program had allocated a page here, so
> + * the lockless probe saw it and we skipped preallocation, but
> it
> + * freed the page before we took the lock. Now we do need one;
> + * sleeping is not allowed here, so fall back to the
> non-blocking
> + * allocator and give up if it fails.
> + */
> + ret = bpf_map_alloc_pages(map, map->numa_node, 1, &new_page);
This isn't a bug, but would it be clearer to describe the condition
generically (the lockless probe's result was invalidated before the lock
was taken) rather than naming only the bpf-alloc-then-free case? The
fallback is also reached if the probe observed arena->scratch_page and the
PTE was cleared by arena_free_pages() before the lock was taken, which is
not a bpf program page allocation.
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/32713810092