* [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes
@ 2026-11-02 6:42 Alexei Starovoitov
2026-11-02 6:42 ` [PATCH bpf 1/2] bpf: Don't move a MAP_FIXED address in arena_get_unmapped_area() Alexei Starovoitov
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Alexei Starovoitov @ 2026-11-02 6:42 UTC (permalink / raw)
To: bpf; +Cc: daniel, andrii, eddyz87, memxor
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 714b7112017f. They answer review comments on that series.
Commit ca580b034380 ("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>
Alexei Starovoitov (2):
bpf: Don't move a MAP_FIXED address in arena_get_unmapped_area()
selftests/bpf: Run arena_mmap serially and test MAP_FIXED addresses
kernel/bpf/arena.c | 13 +++++--
.../selftests/bpf/prog_tests/arena_mmap.c | 36 +++++++++++++------
2 files changed, 37 insertions(+), 12 deletions(-)
base-commit: 714b7112017fc7f8b2afabccf562acdb12d2ea7d
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH bpf 1/2] bpf: Don't move a MAP_FIXED address in arena_get_unmapped_area()
2026-11-02 6:42 [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes Alexei Starovoitov
@ 2026-11-02 6:42 ` Alexei Starovoitov
2026-11-02 6:42 ` [PATCH bpf 2/2] selftests/bpf: Run arena_mmap serially and test MAP_FIXED addresses Alexei Starovoitov
2026-11-02 16:30 ` [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: Alexei Starovoitov @ 2026-11-02 6:42 UTC (permalink / raw)
To: bpf; +Cc: daniel, andrii, eddyz87, memxor
From: Alexei Starovoitov <ast@kernel.org>
The user range of an arena must not cross a 4 GiB boundary. Thus
arena_get_unmapped_area() asks the mm for 2 * len bytes and rounds the
result up if necessary. This is correct only if the mm finds a free range.
With MAP_FIXED the mm does no search and returns addr. For
MAP_FIXED_NOREPLACE, do_mmap() also sets MAP_FIXED.
With MAP_FIXED the arena is not at addr after round_up(). Thus mmap()
removes mappings outside the range from addr to addr + len. It keeps a part
of those in this range. Also, round_up() can wrap to address 0, and the
test for address 0 is before it.
The arch code gets twice the length of the mapping. Thus it can refuse a
valid MAP_FIXED range. On s390 the arch code also grows the page table for
the range. With the double length it can omit this step for a valid range.
The test for a crossing is not exact. Because ret is a long, ret >> 32
extends the sign, but the other side is unsigned. The sides are never equal
for an address with bit 63 set, and round_up() always runs. Such user
addresses exist on s390. arena_map_alloc() does the same test with a u64.
With MAP_FIXED, pass len to the mm. Return -EINVAL if the range crosses a
boundary, as for address 0. Make ret unsigned, so that only these ranges
get the error. On x86-64, if round_up() gives address 0 today, mmap() now
fails with -EINVAL or with -ENOMEM from the mm. Today mmap() can also fail
for the address after round_up(), for example with -ENOMEM, -EEXIST, -EPERM
or -EBUSY.
Without MAP_FIXED the mm must still find a free range of 2 * len bytes
inside the address space of the task. The range after round_up() is inside
it, and round_up() cannot wrap. An address with bit 63 set now moves only
if its range crosses a boundary.
For an arena, libbpf uses MAP_FIXED only with map_extra, and
arena_map_alloc() refuses a map_extra range that crosses a boundary. A
subsequent mmap() must use the range of the first mmap(). For mmap() of the
map fd, this range comes from arena_get_unmapped_area(), thus it crosses no
boundary. arena_map_alloc() still accepts a map_extra above TASK_SIZE, and
mmap() of such an arena fails.
Fixes: 317460317a02 ("bpf: Introduce bpf_arena.")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
kernel/bpf/arena.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 0b1ec3306ea4..ec2bda1a6ebf 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -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);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH bpf 2/2] selftests/bpf: Run arena_mmap serially and test MAP_FIXED addresses
2026-11-02 6:42 [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes Alexei Starovoitov
2026-11-02 6:42 ` [PATCH bpf 1/2] bpf: Don't move a MAP_FIXED address in arena_get_unmapped_area() Alexei Starovoitov
@ 2026-11-02 6:42 ` Alexei Starovoitov
2026-11-02 16:30 ` [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: Alexei Starovoitov @ 2026-11-02 6:42 UTC (permalink / raw)
To: bpf; +Cc: daniel, andrii, eddyz87, memxor
From: Alexei Starovoitov <ast@kernel.org>
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 a14f9ba080c1
("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: 14d3e5bf9ffe ("selftests/bpf: Test the length and the address of an arena mmap()")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
.../selftests/bpf/prog_tests/arena_mmap.c | 36 +++++++++++++------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_mmap.c b/tools/testing/selftests/bpf/prog_tests/arena_mmap.c
index 2babc9ecf182..f55d950720f1 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_mmap.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_mmap.c
@@ -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);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes
2026-11-02 6:42 [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes Alexei Starovoitov
2026-11-02 6:42 ` [PATCH bpf 1/2] bpf: Don't move a MAP_FIXED address in arena_get_unmapped_area() Alexei Starovoitov
2026-11-02 6:42 ` [PATCH bpf 2/2] selftests/bpf: Run arena_mmap serially and test MAP_FIXED addresses Alexei Starovoitov
@ 2026-11-02 16:30 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-11-02 16:30 UTC (permalink / raw)
To: Alexei Starovoitov; +Cc: bpf, daniel, andrii, eddyz87, memxor
Hello:
This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Mon, 2 Nov 2026 06:42:29 +0000 you wrote:
> 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 714b7112017f. They answer review comments on that series.
>
> Commit ca580b034380 ("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.
>
> [...]
Here is the summary with links:
- [bpf,1/2] bpf: Don't move a MAP_FIXED address in arena_get_unmapped_area()
https://git.kernel.org/bpf/bpf/c/92b5ce3ca5b7
- [bpf,2/2] selftests/bpf: Run arena_mmap serially and test MAP_FIXED addresses
https://git.kernel.org/bpf/bpf/c/c08b50dd174a
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-02 16:30 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-02 6:42 [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes Alexei Starovoitov
2026-11-02 6:42 ` [PATCH bpf 1/2] bpf: Don't move a MAP_FIXED address in arena_get_unmapped_area() Alexei Starovoitov
2026-11-02 6:42 ` [PATCH bpf 2/2] selftests/bpf: Run arena_mmap serially and test MAP_FIXED addresses Alexei Starovoitov
2026-11-02 16:30 ` [PATCH bpf 0/2] bpf: Follow-ups for the arena mmap() fixes patchwork-bot+netdevbpf
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.