Skip to content

Commit 201128f

Browse files
Alexei Starovoitovgregkh
authored andcommitted
bpf: Fix use-after-free in arena_vm_close on fork
commit 4fddde2 upstream. 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: 3174603 ("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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 5b906b3 commit 201128f

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

kernel/bpf/arena.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,16 @@ static void arena_vm_open(struct vm_area_struct *vma)
341341
refcount_inc(&vml->mmap_count);
342342
}
343343

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+
344354
static void arena_vm_close(struct vm_area_struct *vma)
345355
{
346356
struct bpf_map *map = vma->vm_file->private_data;
@@ -417,6 +427,8 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
417427

418428
static const struct vm_operations_struct arena_vm_ops = {
419429
.open = arena_vm_open,
430+
.may_split = arena_vm_may_split,
431+
.mremap = arena_vm_mremap,
420432
.close = arena_vm_close,
421433
.fault = arena_vm_fault,
422434
};
@@ -486,10 +498,11 @@ static int arena_map_mmap(struct bpf_map *map, struct vm_area_struct *vma)
486498
arena->user_vm_end = vma->vm_end;
487499
/*
488500
* bpf_map_mmap() checks that it's being mmaped as VM_SHARED and
489-
* clears VM_MAYEXEC. Set VM_DONTEXPAND as well to avoid
490-
* potential change of user_vm_start.
501+
* clears VM_MAYEXEC. Set VM_DONTEXPAND to avoid potential change
502+
* of user_vm_start. Set VM_DONTCOPY to prevent arena VMA from
503+
* being copied into the child process on fork.
491504
*/
492-
vm_flags_set(vma, VM_DONTEXPAND);
505+
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTCOPY);
493506
vma->vm_ops = &arena_vm_ops;
494507
return 0;
495508
}

0 commit comments

Comments
 (0)