Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

bpf: Fix use-after-free in arena_vm_close on fork

arena_vm_open() only bumps vml->mmap_count but never registers the
child VMA in arena->vma_list. The vml->vma always points at the
parent VMA, so after parent munmap the pointer dangles. If the child
then calls bpf_arena_free_pages(), zap_pages() reads the stale
vml->vma triggering use-after-free.

Fix this by preventing the arena VMA from being inherited across
fork with VM_DONTCOPY, and preventing VMA splits via the may_split
callback.

Also reject mremap with a .mremap callback returning -EINVAL. A
same-size mremap(MREMAP_FIXED) on the full arena VMA reaches
copy_vma() through the following path:

check_prep_vma() - returns 0 early: new_len == old_len
skips VM_DONTEXPAND check
prep_move_vma() - vm_start == old_addr and
vm_end == old_addr + old_len
so may_split is never called
move_vma()
copy_vma_and_data()
copy_vma()
vm_area_dup() - copies vm_private_data (vml pointer)
vm_ops->open() - bumps vml->mmap_count
vm_ops->mremap() - returns -EINVAL, rollback unmaps new VMA

The refcount ensures the rollback's arena_vm_close does not free
the vml shared with the original VMA.

Reported-by: Weiming Shi <bestswngs@gmail.com>
Reported-by: Xiang Mei <xmei5@asu.edu>
Fixes: 317460317a02 ("bpf: Introduce bpf_arena.")
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Link: https://lore.kernel.org/r/20260413194245.21449-1-alexei.starovoitov@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+16 -3
+16 -3
kernel/bpf/arena.c
··· 341 341 refcount_inc(&vml->mmap_count); 342 342 } 343 343 344 + static int arena_vm_may_split(struct vm_area_struct *vma, unsigned long addr) 345 + { 346 + return -EINVAL; 347 + } 348 + 349 + static int arena_vm_mremap(struct vm_area_struct *vma) 350 + { 351 + return -EINVAL; 352 + } 353 + 344 354 static void arena_vm_close(struct vm_area_struct *vma) 345 355 { 346 356 struct bpf_map *map = vma->vm_file->private_data; ··· 427 417 428 418 static const struct vm_operations_struct arena_vm_ops = { 429 419 .open = arena_vm_open, 420 + .may_split = arena_vm_may_split, 421 + .mremap = arena_vm_mremap, 430 422 .close = arena_vm_close, 431 423 .fault = arena_vm_fault, 432 424 }; ··· 498 486 arena->user_vm_end = vma->vm_end; 499 487 /* 500 488 * bpf_map_mmap() checks that it's being mmaped as VM_SHARED and 501 - * clears VM_MAYEXEC. Set VM_DONTEXPAND as well to avoid 502 - * potential change of user_vm_start. 489 + * clears VM_MAYEXEC. Set VM_DONTEXPAND to avoid potential change 490 + * of user_vm_start. Set VM_DONTCOPY to prevent arena VMA from 491 + * being copied into the child process on fork. 503 492 */ 504 - vm_flags_set(vma, VM_DONTEXPAND); 493 + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTCOPY); 505 494 vma->vm_ops = &arena_vm_ops; 506 495 return 0; 507 496 }