selftests/bpf: Run arena_mmap serially and test MAP_FIXED addresses

Each arena reserves 4 GiB of vmalloc space. The KASAN shadow of arenas from
parallel tests can exhaust the memory of a CI VM. Thus commit a14f9ba080
("selftests/bpf: Run arena tests serially") made arena_* and libarena*
serial. Do the same for arena_mmap, which the commit below added later.

Add four subtests for "bpf: Don't move a MAP_FIXED address in
arena_get_unmapped_area()". Test that mmap() with MAP_FIXED or
MAP_FIXED_NOREPLACE fails with EINVAL if the range crosses a 4 GiB
boundary. Test that mmap() with MAP_FIXED also fails for a range that wraps
and for a map_extra range that ends above TASK_SIZE. Today
arena_map_alloc() accepts such a map_extra. map_extra_last_pages also
passes if the kernel refuses the map, because no task can map this range.

The arch code gets a range before the arena code tests it for a crossing.
For the last two ranges LoongArch returns -EINVAL, and powerpc with the
hash MMU returns -ENOMEM. There the two subtests do not reach that test. On
the other architectures with arenas, the arena code returns -EINVAL or
__get_unmapped_area() returns -ENOMEM. Thus the two subtests accept both
errnos. All these errors occur before mmap() changes a mapping.

Two other changes of that patch have no subtest. With MAP_FIXED the arch
code gets len and not 2 * len. A 64-bit task on x86, arm64 or riscv does
not see this. LoongArch, s390 and powerpc with the hash MMU compare the end
of the range with different limits. In arena_mmap the unsigned ret changes
only the errno of map_extra_last_pages, which accepts both.

Fixes: 14d3e5bf9f ("selftests/bpf: Test the length and the address of an arena mmap()")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261102064231.2153879-3-alexei.starovoitov@gmail.com
This commit is contained in:
Alexei Starovoitov 2026-11-02 06:42:31 +00:00
parent 92b5ce3ca5
commit c08b50dd17
No known key found for this signature in database

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);
}