From: Yuan Chen arena_free_pages() and arena_free_worker() ignore range_tree_set() errors and proceed to clear PTEs and free pages, even though the range tree still considers the range allocated. The range then becomes permanently unavailable. Check the return value: in arena_free_pages() abort the free on error; in arena_free_worker() move range_tree_set() before PTE clearing and skip the span on error. Fixes: b795379757eb ("bpf: Introduce range_tree data structure and use it in bpf arena") Signed-off-by: Yuan Chen --- kernel/bpf/arena.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index 555ee2531ef9..30745da9277b 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -881,7 +881,12 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, if (ret) goto defer; - range_tree_set(&arena->rt, pgoff, page_cnt); + ret = range_tree_set(&arena->rt, pgoff, page_cnt); + if (ret) { + raw_res_spin_unlock_irqrestore(&arena->spinlock, flags); + bpf_map_memcg_exit(old_memcg, new_memcg); + return; + } init_llist_head(&free_pages); cdata.arena = arena; @@ -1004,11 +1009,17 @@ static void arena_free_worker(struct work_struct *work) kaddr = arena_vm_start + s->uaddr; pgoff = compute_pgoff(arena, s->uaddr); + /* Mark the range as free before clearing PTEs so that + * if the range tree update fails we leave the PTEs + * intact, avoiding an unrecoverable state where pages + * are freed but the arena free tree does not track them. + */ + if (range_tree_set(&arena->rt, pgoff, page_cnt)) + continue; + /* clear ptes and collect pages in free_pages llist */ apply_to_existing_page_range(&init_mm, kaddr, page_cnt << PAGE_SHIFT, apply_range_clear_cb, &cdata); - - range_tree_set(&arena->rt, pgoff, page_cnt); } raw_res_spin_unlock_irqrestore(&arena->spinlock, flags); -- 2.54.0