Merge branch 'bpf-follow-ups-for-the-arena-mmap-fixes'

Alexei Starovoitov says:

====================
bpf: Follow-ups for the arena mmap() fixes

From: Alexei Starovoitov <ast@kernel.org>

Two follow-ups for the arena patches of the series
"bpf: Fixes for the verifier, arena, skb and lwt helpers", merge
commit 714b711201. They answer review comments on that series.

Commit ca580b0343 ("bpf: Refuse to map an arena at address 0") tests for
address 0 in front of round_up(), and round_up() can wrap to 0. Patch 1
does not move a MAP_FIXED address at all. Patch 2 makes arena_mmap serial,
as the other arena tests are, and adds subtests for patch 1.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
====================

Link: https://patch.msgid.link/20261102064231.2153879-1-alexei.starovoitov@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-11-02 16:22:44 +00:00
commit 03b0138974
No known key found for this signature in database
2 changed files with 37 additions and 12 deletions

View File

@ -7,6 +7,7 @@
#include <linux/irq_work.h>
#include "linux/filter.h"
#include <linux/llist.h>
#include <linux/mman.h>
#include <linux/btf_ids.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
@ -630,7 +631,7 @@ static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long ad
{
struct bpf_map *map = filp->private_data;
struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
long ret;
unsigned long ret;
if (pgoff)
return -EINVAL;
@ -647,7 +648,13 @@ static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long ad
return -EINVAL;
}
ret = mm_get_unmapped_area(filp, addr, len * 2, 0, flags);
/*
* Without MAP_FIXED the mm finds a free range. Ask for twice the
* length, so that there is room for round_up(). With MAP_FIXED the mm
* does no search, and the caller wants this address. Thus the range
* cannot move.
*/
ret = mm_get_unmapped_area(filp, addr, flags & MAP_FIXED ? len : len * 2, 0, flags);
if (IS_ERR_VALUE(ret))
return ret;
if (!ret)
@ -655,6 +662,8 @@ static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long ad
return -EINVAL;
if ((ret >> 32) == ((ret + len - 1) >> 32))
return ret;
if (flags & MAP_FIXED)
return -EINVAL;
return round_up(ret, SZ_4G);
}

View File

@ -5,9 +5,12 @@
#define ARENA_PAGES 8
static void test_refused(int pages, int flags, int err)
/* err == 0: the range ends above TASK_SIZE. EINVAL or ENOMEM, this depends on the arch */
static void test_refused(__u64 map_extra, __u64 addr, int pages, int flags, int err)
{
LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE);
LIBBPF_OPTS(bpf_map_create_opts, opts,
.map_flags = BPF_F_MMAPABLE,
.map_extra = map_extra);
void *area;
int fd;
@ -16,22 +19,35 @@ static void test_refused(int pages, int flags, int err)
test__skip();
return;
}
/* No task can map this range, so the kernel can also refuse the map. */
if (fd < 0 && map_extra)
return;
if (!ASSERT_GE(fd, 0, "bpf_map_create"))
return;
area = mmap(NULL, pages * getpagesize(), PROT_READ | PROT_WRITE,
MAP_SHARED | flags, fd, 0);
if (ASSERT_EQ(area, MAP_FAILED, "mmap"))
ASSERT_EQ(errno, err, "errno");
else
area = mmap((void *)(unsigned long)addr, pages * getpagesize(),
PROT_READ | PROT_WRITE, MAP_SHARED | flags, fd, 0);
if (!ASSERT_EQ(area, MAP_FAILED, "mmap"))
munmap(area, pages * getpagesize());
else if (err || errno != ENOMEM)
ASSERT_EQ(errno, err ?: EINVAL, "errno");
close(fd);
}
void test_arena_mmap(void)
void serial_test_arena_mmap(void)
{
__u64 page = getpagesize(), last = -(ARENA_PAGES * page);
if (test__start_subtest("long"))
test_refused(ARENA_PAGES + 1, 0, E2BIG);
test_refused(0, 0, ARENA_PAGES + 1, 0, E2BIG);
if (test__start_subtest("address_0"))
test_refused(ARENA_PAGES, MAP_FIXED, EINVAL);
test_refused(0, 0, ARENA_PAGES, MAP_FIXED, EINVAL);
if (test__start_subtest("crosses_4g"))
test_refused(0, (1ULL << 32) - page, ARENA_PAGES, MAP_FIXED, EINVAL);
if (test__start_subtest("crosses_4g_noreplace"))
test_refused(0, (1ULL << 32) - page, ARENA_PAGES, MAP_FIXED_NOREPLACE, EINVAL);
if (test__start_subtest("wraps"))
test_refused(0, -page, ARENA_PAGES, MAP_FIXED, 0);
if (test__start_subtest("map_extra_last_pages"))
test_refused(last, last, ARENA_PAGES, MAP_FIXED, 0);
}