bpf: Fix remap of arena.
authorAlexei Starovoitov <ast@kernel.org>
Mon, 17 Jun 2024 17:18:12 +0000 (10:18 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Tue, 18 Jun 2024 15:19:46 +0000 (17:19 +0200)
The bpf arena logic didn't account for mremap operation. Add a refcnt for
multiple mmap events to prevent use-after-free in arena_vm_close.

Fixes: 317460317a02 ("bpf: Introduce bpf_arena.")
Reported-by: Pengfei Xu <pengfei.xu@intel.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Barret Rhoden <brho@google.com>
Tested-by: Pengfei Xu <pengfei.xu@intel.com>
Closes: https://lore.kernel.org/bpf/Zmuw29IhgyPNKnIM@xpf.sh.intel.com
Link: https://lore.kernel.org/bpf/20240617171812.76634-1-alexei.starovoitov@gmail.com
kernel/bpf/arena.c

index 583ee4fe48efd518f5fe59ad610d60962af7f325..e52b3ad231b9c4f2d7fe582ea8c28d9fcffdc5be 100644 (file)
@@ -212,6 +212,7 @@ static u64 arena_map_mem_usage(const struct bpf_map *map)
 struct vma_list {
        struct vm_area_struct *vma;
        struct list_head head;
+       atomic_t mmap_count;
 };
 
 static int remember_vma(struct bpf_arena *arena, struct vm_area_struct *vma)
@@ -221,20 +222,30 @@ static int remember_vma(struct bpf_arena *arena, struct vm_area_struct *vma)
        vml = kmalloc(sizeof(*vml), GFP_KERNEL);
        if (!vml)
                return -ENOMEM;
+       atomic_set(&vml->mmap_count, 1);
        vma->vm_private_data = vml;
        vml->vma = vma;
        list_add(&vml->head, &arena->vma_list);
        return 0;
 }
 
+static void arena_vm_open(struct vm_area_struct *vma)
+{
+       struct vma_list *vml = vma->vm_private_data;
+
+       atomic_inc(&vml->mmap_count);
+}
+
 static void arena_vm_close(struct vm_area_struct *vma)
 {
        struct bpf_map *map = vma->vm_file->private_data;
        struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
-       struct vma_list *vml;
+       struct vma_list *vml = vma->vm_private_data;
 
+       if (!atomic_dec_and_test(&vml->mmap_count))
+               return;
        guard(mutex)(&arena->lock);
-       vml = vma->vm_private_data;
+       /* update link list under lock */
        list_del(&vml->head);
        vma->vm_private_data = NULL;
        kfree(vml);
@@ -287,6 +298,7 @@ out:
 }
 
 static const struct vm_operations_struct arena_vm_ops = {
+       .open           = arena_vm_open,
        .close          = arena_vm_close,
        .fault          = arena_vm_fault,
 };