Skip to content

Commit d856d02

Browse files
etsalKernel Patches Daemon
authored andcommitted
bpf/arena: add bpf_arena_guard_pages kfunc
Add a new BPF arena kfunc from protecting a range of pages. These pages cannot be allocated, either explicitly through bpf_arena_alloc_pages() or implicitly through userspace page faults. Signed-off-by: Emil Tsalapatis <[email protected]>
1 parent 4516bd2 commit d856d02

File tree

1 file changed

+92
-3
lines changed

1 file changed

+92
-3
lines changed

kernel/bpf/arena.c

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct bpf_arena {
4848
u64 user_vm_end;
4949
struct vm_struct *kern_vm;
5050
struct range_tree rt;
51+
struct range_tree rt_guard;
5152
struct list_head vma_list;
5253
struct mutex lock;
5354
};
@@ -143,6 +144,20 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr)
143144
bpf_map_area_free(arena);
144145
goto err;
145146
}
147+
148+
/*
149+
* Use the same semantics as the main range tree to reuse
150+
* its methods: Present ranges are all unguarded, while
151+
* absent ones are guarded.
152+
*/
153+
range_tree_init(&arena->rt_guard);
154+
err = range_tree_set(&arena->rt_guard, 0, attr->max_entries);
155+
if (err) {
156+
range_tree_destroy(&arena->rt);
157+
bpf_map_area_free(arena);
158+
goto err;
159+
}
160+
146161
mutex_init(&arena->lock);
147162

148163
return &arena->map;
@@ -193,6 +208,7 @@ static void arena_map_free(struct bpf_map *map)
193208
apply_to_existing_page_range(&init_mm, bpf_arena_get_kern_vm_start(arena),
194209
KERN_VM_SZ - GUARD_SZ, existing_page_cb, NULL);
195210
free_vm_area(arena->kern_vm);
211+
range_tree_destroy(&arena->rt_guard);
196212
range_tree_destroy(&arena->rt);
197213
bpf_map_area_free(arena);
198214
}
@@ -282,6 +298,11 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
282298
/* User space requested to segfault when page is not allocated by bpf prog */
283299
return VM_FAULT_SIGSEGV;
284300

301+
/* Make sure the page is not guarded. */
302+
ret = is_range_tree_set(&arena->rt_guard, vmf->pgoff, 1);
303+
if (ret)
304+
return VM_FAULT_SIGSEGV;
305+
285306
ret = range_tree_clear(&arena->rt, vmf->pgoff, 1);
286307
if (ret)
287308
return VM_FAULT_SIGSEGV;
@@ -456,12 +477,17 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt
456477
ret = is_range_tree_set(&arena->rt, pgoff, page_cnt);
457478
if (ret)
458479
goto out_free_pages;
459-
ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
460480
} else {
461481
ret = pgoff = range_tree_find(&arena->rt, page_cnt);
462-
if (pgoff >= 0)
463-
ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
482+
if (pgoff < 0)
483+
goto out_free_pages;
464484
}
485+
486+
ret = is_range_tree_set(&arena->rt_guard, pgoff, page_cnt);
487+
if (ret)
488+
goto out_free_pages;
489+
490+
ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
465491
if (ret)
466492
goto out_free_pages;
467493

@@ -512,6 +538,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
512538
u64 full_uaddr, uaddr_end;
513539
long kaddr, pgoff, i;
514540
struct page *page;
541+
int ret;
515542

516543
/* only aligned lower 32-bit are relevant */
517544
uaddr = (u32)uaddr;
@@ -525,7 +552,14 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
525552

526553
guard(mutex)(&arena->lock);
527554

555+
528556
pgoff = compute_pgoff(arena, uaddr);
557+
558+
/* Do not free regions that include guarded pages. */
559+
ret = is_range_tree_set(&arena->rt_guard, pgoff, page_cnt);
560+
if (ret)
561+
return;
562+
529563
/* clear range */
530564
range_tree_set(&arena->rt, pgoff, page_cnt);
531565

@@ -550,6 +584,46 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
550584
}
551585
}
552586

587+
static int arena_guard_pages(struct bpf_arena *arena, long uaddr, u32 page_cnt)
588+
{
589+
long page_cnt_max = (arena->user_vm_end - arena->user_vm_start) >> PAGE_SHIFT;
590+
long pgoff;
591+
int ret;
592+
593+
if (uaddr & ~PAGE_MASK)
594+
return 0;
595+
596+
pgoff = compute_pgoff(arena, uaddr);
597+
if (pgoff + page_cnt > page_cnt_max)
598+
return -EINVAL;
599+
600+
guard(mutex)(&arena->lock);
601+
602+
/* Make sure we have not already guarded the pages. */
603+
ret = is_range_tree_set(&arena->rt_guard, pgoff, page_cnt);
604+
if (ret)
605+
return -EALREADY;
606+
607+
/* Cannot guard already allocated pages. */
608+
ret = is_range_tree_set(&arena->rt, pgoff, page_cnt);
609+
if (ret)
610+
return -EINVAL;
611+
612+
/* Reserve the region. */
613+
ret = range_tree_clear(&arena->rt_guard, pgoff, page_cnt);
614+
if (ret)
615+
return ret;
616+
617+
/* Also "allocate" the region to prevent it from being allocated. */
618+
ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
619+
if (ret) {
620+
range_tree_set(&arena->rt_guard, pgoff, page_cnt);
621+
return ret;
622+
}
623+
624+
return 0;
625+
}
626+
553627
__bpf_kfunc_start_defs();
554628

555629
__bpf_kfunc void *bpf_arena_alloc_pages(void *p__map, void *addr__ign, u32 page_cnt,
@@ -573,11 +647,26 @@ __bpf_kfunc void bpf_arena_free_pages(void *p__map, void *ptr__ign, u32 page_cnt
573647
return;
574648
arena_free_pages(arena, (long)ptr__ign, page_cnt);
575649
}
650+
651+
__bpf_kfunc int bpf_arena_guard_pages(void *p__map, void *ptr__ign, u32 page_cnt)
652+
{
653+
struct bpf_map *map = p__map;
654+
struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
655+
656+
if (map->map_type != BPF_MAP_TYPE_ARENA)
657+
return -EINVAL;
658+
659+
if (!page_cnt)
660+
return 0;
661+
662+
return arena_guard_pages(arena, (long)ptr__ign, page_cnt);
663+
}
576664
__bpf_kfunc_end_defs();
577665

578666
BTF_KFUNCS_START(arena_kfuncs)
579667
BTF_ID_FLAGS(func, bpf_arena_alloc_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_RET | KF_ARENA_ARG2)
580668
BTF_ID_FLAGS(func, bpf_arena_free_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_ARG2)
669+
BTF_ID_FLAGS(func, bpf_arena_guard_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_ARG2)
581670
BTF_KFUNCS_END(arena_kfuncs)
582671

583672
static const struct btf_kfunc_id_set common_kfunc_set = {

0 commit comments

Comments
 (0)