From: "Emil Tsalapatis" <emil@etsalapatis.com>
To: "Weiming Shi" <bestswngs@gmail.com>,
"Alexei Starovoitov" <ast@kernel.org>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Andrii Nakryiko" <andrii@kernel.org>
Cc: "Martin KaFai Lau" <martin.lau@linux.dev>,
"Eduard Zingerman" <eddyz87@gmail.com>,
"Song Liu" <song@kernel.org>,
"Yonghong Song" <yonghong.song@linux.dev>,
"John Fastabend" <john.fastabend@gmail.com>,
"KP Singh" <kpsingh@kernel.org>,
"Stanislav Fomichev" <sdf@fomichev.me>,
"Hao Luo" <haoluo@google.com>, "Jiri Olsa" <jolsa@kernel.org>,
"Barret Rhoden" <brho@google.com>, <bpf@vger.kernel.org>,
<linux-kernel@vger.kernel.org>, "Xiang Mei" <xmei5@asu.edu>
Subject: Re: [PATCH bpf v2 2/2] selftests/bpf: Add test for arena VMA use-after-free on fork
Date: Sat, 11 Apr 2026 13:33:43 -0400 [thread overview]
Message-ID: <DHQI86CY1QDQ.16RMS5QSX99EC@etsalapatis.com> (raw)
In-Reply-To: <20260411112944.1455470-3-bestswngs@gmail.com>
On Sat Apr 11, 2026 at 7:29 AM EDT, Weiming Shi wrote:
> Add a selftest that reproduces the arena VMA use-after-free fixed in
> the previous commit. The test creates an arena, mmaps it, allocates
> pages via BPF, forks, has the parent munmap the arena, then has the
> child call bpf_arena_free_pages. Without the fix this triggers a
> KASAN slab-use-after-free in zap_page_range_single.
>
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> ---
> .../selftests/bpf/prog_tests/arena_fork.c | 86 +++++++++++++++++++
> .../testing/selftests/bpf/progs/arena_fork.c | 41 +++++++++
> 2 files changed, 127 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/arena_fork.c
> create mode 100644 tools/testing/selftests/bpf/progs/arena_fork.c
>
The test doesn't work for me as advertised. Does it fail for you under
vmtest without patch 1/2?
The test doesn't fail on base vmtest for me, even without the previous patch,
because KASAN isn't turned on for the CI. With KASAN the test triggers
the splat just fine.
Should we maybe turn on KASAN and panic_on_warn by default on vmtest?
> diff --git a/tools/testing/selftests/bpf/prog_tests/arena_fork.c b/tools/testing/selftests/bpf/prog_tests/arena_fork.c
> new file mode 100644
> index 000000000000..445574827891
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/arena_fork.c
> @@ -0,0 +1,86 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 */
> +
No actual copyright holder.
> +/*
> + * Test that forking a process with an arena mmap does not cause a
> + * use-after-free when the parent unmaps and the child frees arena pages.
> + *
> + * The bug: arena_vm_open() only incremented a refcount but never registered
> + * the child's VMA. After parent munmap, vml->vma pointed to a freed
> + * vm_area_struct. bpf_arena_free_pages -> zap_pages would then UAF.
Remove this, there is no point keeping information about past bugs in
the tests. The first sentence is enough.
> + */
> +#include <test_progs.h>
> +#include <sys/mman.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +#include <sys/user.h>
> +#ifndef PAGE_SIZE
> +#define PAGE_SIZE getpagesize()
> +#endif
Use sysconf to get the page size.
> +
> +#include "arena_fork.skel.h"
> +
> +void test_arena_fork(void)
> +{
> + LIBBPF_OPTS(bpf_test_run_opts, opts);
> + struct bpf_map_info info = {};
> + __u32 info_len = sizeof(info);
> + struct arena_fork *skel;
> + size_t arena_sz;
> + void *arena_addr;
> + int arena_fd, ret, status;
> + pid_t pid;
> +
> + skel = arena_fork__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "open_and_load"))
> + return;
> +
> + arena_fd = bpf_map__fd(skel->maps.arena);
> +
> + /* libbpf mmaps the arena via initial_value */
> + arena_addr = bpf_map__initial_value(skel->maps.arena, &arena_sz);
> + if (!ASSERT_OK_PTR(arena_addr, "arena_mmap"))
> + goto out;
> +
> + /* Get real arena byte size for munmap */
> + bpf_map_get_info_by_fd(arena_fd, &info, &info_len);
> + arena_sz = (size_t)info.max_entries * PAGE_SIZE;
> +
> + /* Allocate 4 pages in the arena via BPF */
> + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.arena_alloc),
> + &opts);
> + if (!ASSERT_OK(ret, "alloc_run") ||
> + !ASSERT_OK(opts.retval, "alloc_ret"))
> + goto out;
> +
> + /* Fault in a page so zap_pages has work to do */
> + ((char *)arena_addr)[0] = 'A';
> +
> + /* Fork: child inherits the arena VMA */
> + pid = fork();
> + if (!ASSERT_GE(pid, 0, "fork"))
> + goto out;
> +
> + if (pid == 0) {
> + /* Child: parent will unmap first, then we free pages.
> + * Without the fix, this triggers UAF in zap_pages.
Again, "the fix" isn't clear. Remove the second sentence.
> + */
> + LIBBPF_OPTS(bpf_test_run_opts, child_opts);
> + int free_fd = bpf_program__fd(skel->progs.arena_free);
> +
> + usleep(200000); /* let parent munmap first */
This is dependable on vmtest, and we do use usleep in other tests
so I'd say it's a valid use.
> +
> + ret = bpf_prog_test_run_opts(free_fd, &child_opts);
> + _exit(ret || child_opts.retval);
> + }
> +
> + /* Parent: unmap the arena, making vml->vma stale */
> + munmap(arena_addr, arena_sz);
> +
> + /* Wait for child -- if kernel UAFs, child will crash/hang */
That's not the case. The test exits with a success for me.
> + waitpid(pid, &status, 0);
> + ASSERT_TRUE(WIFEXITED(status), "child_exited");
> + ASSERT_EQ(WEXITSTATUS(status), 0, "child_exit_code");
> +out:
> + arena_fork__destroy(skel);
> +}
> diff --git a/tools/testing/selftests/bpf/progs/arena_fork.c b/tools/testing/selftests/bpf/progs/arena_fork.c
> new file mode 100644
> index 000000000000..b1f8435f1834
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/arena_fork.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 */
There's no copyright holder here, add one or remove the line it.
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_arena_common.h"
> +
> +struct {
> + __uint(type, BPF_MAP_TYPE_ARENA);
> + __uint(map_flags, BPF_F_MMAPABLE);
> + __uint(max_entries, 16); /* number of pages */
> +#ifdef __TARGET_ARCH_arm64
> + __ulong(map_extra, 0x1ull << 32); /* start of mmap() region */
> +#else
> + __ulong(map_extra, 0x1ull << 44); /* start of mmap() region */
> +#endif
> +} arena SEC(".maps");
> +
> +void __arena *alloc_addr;
> +
> +SEC("syscall")
> +int arena_alloc(void *ctx)
> +{
> + void __arena *p;
> +
> + p = bpf_arena_alloc_pages(&arena, NULL, 4, NUMA_NO_NODE, 0);
> + if (!p)
> + return 1;
> + alloc_addr = p;
> + return 0;
> +}
> +
> +SEC("syscall")
> +int arena_free(void *ctx)
> +{
> + if (!alloc_addr)
> + return 1;
> + bpf_arena_free_pages(&arena, alloc_addr, 4);
> + return 0;
> +}
> +
> +char _license[] SEC("license") = "GPL";
prev parent reply other threads:[~2026-04-11 17:33 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-11 11:29 [PATCH bpf v2 0/2] bpf: Fix arena VMA use-after-free on fork Weiming Shi
2026-04-11 11:29 ` [PATCH bpf v2 1/2] bpf: Fix use-after-free of arena VMA " Weiming Shi
2026-04-11 17:57 ` Emil Tsalapatis
2026-04-11 11:29 ` [PATCH bpf v2 2/2] selftests/bpf: Add test for arena VMA use-after-free " Weiming Shi
2026-04-11 17:33 ` Emil Tsalapatis [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DHQI86CY1QDQ.16RMS5QSX99EC@etsalapatis.com \
--to=emil@etsalapatis.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bestswngs@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=brho@google.com \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=haoluo@google.com \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=sdf@fomichev.me \
--cc=song@kernel.org \
--cc=xmei5@asu.edu \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox