All of lore.kernel.org
 help / color / mirror / Atom feed
* [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.