From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Eduard Zingerman <eddyz87@gmail.com>,
Emil Tsalapatis <emil@etsalapatis.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
Dave Hansen <dave.hansen@linux.intel.com>,
Andy Lutomirski <luto@kernel.org>,
Peter Zijlstra <peterz@infradead.org>,
Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
Borislav Petkov <bp@alien8.de>,
Puranjay Mohan <puranjay@kernel.org>,
kkd@meta.com, kernel-team@meta.com, x86@kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH bpf-next v1 3/3] selftests/bpf: Test PROBE_MEM loads from invalid addresses
Date: Fri, 9 Oct 2026 04:49:23 +0200 [thread overview]
Message-ID: <20261009024925.3169077-4-memxor@gmail.com> (raw)
In-Reply-To: <20261009024925.3169077-1-memxor@gmail.com>
Add a test that performs PROBE_MEM loads of three sizes through a
bpf_core_cast() pointer whose value is chosen by userspace: NULL, a low
user address, the last user page, a non-canonical address and, on x86-64,
the vsyscall page and an offset into it. Each load must read zero and the
kernel must survive. A load through the current task pointer checks that
the same loads read real values when the address is valid.
The test passes on kernels that reject the addresses with the JIT's range
check and on kernels that rely on the fault handler; with only the JIT
change of the previous patch applied, the first subtest oopses, which is
what the fault handler change prevents.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../bpf/prog_tests/probe_mem_fault.c | 69 +++++++++++++++++++
.../selftests/bpf/progs/probe_mem_fault.c | 41 +++++++++++
2 files changed, 110 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/probe_mem_fault.c
create mode 100644 tools/testing/selftests/bpf/progs/probe_mem_fault.c
diff --git a/tools/testing/selftests/bpf/prog_tests/probe_mem_fault.c b/tools/testing/selftests/bpf/prog_tests/probe_mem_fault.c
new file mode 100644
index 000000000000..57a313e35eb6
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/probe_mem_fault.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include "probe_mem_fault.skel.h"
+
+#if defined(__x86_64__)
+#include <asm/vsyscall.h>
+#endif
+
+/*
+ * Addresses a PROBE_MEM load has to survive. Either the JIT's address check
+ * or the fault handler must turn each load into a zero result.
+ */
+static const struct {
+ const char *name;
+ unsigned long addr;
+} bad_addrs[] = {
+ { "null", 0 },
+ { "low_user", 4096 },
+ { "last_user_page", (1UL << 47) - 4096 },
+ { "non_canonical", 1UL << 63 },
+#if defined(__x86_64__)
+ { "vsyscall", VSYSCALL_ADDR },
+ { "vsyscall_tail", VSYSCALL_ADDR + 0x800 },
+#endif
+};
+
+static void trigger(struct probe_mem_fault *skel, int *runs)
+{
+ skel->bss->val_dw = ~0ULL;
+ skel->bss->val_w = ~0U;
+ skel->bss->val_b = ~0;
+ usleep(1);
+ ASSERT_EQ(skel->bss->runs, ++*runs, "runs");
+}
+
+void test_probe_mem_fault(void)
+{
+ struct probe_mem_fault *skel;
+ int runs = 0, i;
+
+ skel = probe_mem_fault__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ skel->bss->target_pid = getpid();
+ if (!ASSERT_OK(probe_mem_fault__attach(skel), "attach"))
+ goto out;
+
+ /* A valid kernel address is read for real. */
+ skel->bss->use_current_task = true;
+ trigger(skel, &runs);
+ ASSERT_EQ(skel->bss->val_w, getpid(), "pid");
+ ASSERT_NEQ(skel->bss->val_dw, 0, "start_time");
+ ASSERT_NEQ(skel->bss->val_b, 0, "comm");
+
+ skel->bss->use_current_task = false;
+ for (i = 0; i < ARRAY_SIZE(bad_addrs); i++) {
+ if (!test__start_subtest(bad_addrs[i].name))
+ continue;
+ skel->bss->addr = bad_addrs[i].addr;
+ trigger(skel, &runs);
+ ASSERT_EQ(skel->bss->val_dw, 0, "start_time");
+ ASSERT_EQ(skel->bss->val_w, 0, "pid");
+ ASSERT_EQ(skel->bss->val_b, 0, "comm");
+ }
+out:
+ probe_mem_fault__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/probe_mem_fault.c b/tools/testing/selftests/bpf/progs/probe_mem_fault.c
new file mode 100644
index 000000000000..748be45418f5
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/probe_mem_fault.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_misc.h"
+
+char _license[] SEC("license") = "GPL";
+
+int target_pid;
+bool use_current_task;
+unsigned long addr;
+int runs;
+__u64 val_dw;
+__u32 val_w;
+__u8 val_b;
+
+SEC("fentry/" SYS_PREFIX "sys_nanosleep")
+int probe_mem_fault(void *ctx)
+{
+ struct task_struct *task;
+ unsigned long p = addr;
+
+ if ((bpf_get_current_pid_tgid() >> 32) != target_pid)
+ return 0;
+
+ if (use_current_task)
+ p = (unsigned long)bpf_get_current_task_btf();
+ /*
+ * bpf_core_cast() yields an untrusted pointer, so every load through it
+ * is a PROBE_MEM load. Whatever the address is, the load must either
+ * read the field or produce zero; the kernel must not oops.
+ */
+ task = bpf_core_cast((void *)p, struct task_struct);
+ val_dw = task->start_time;
+ val_w = task->pid;
+ val_b = task->comm[0];
+ runs++;
+ return 0;
+}
--
2.53.0-Meta
prev parent reply other threads:[~2026-10-09 2:49 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 2:49 [PATCH bpf-next v1 0/3] bpf, x86: Drop the PROBE_MEM address range check under SMAP Kumar Kartikeya Dwivedi
2026-10-09 2:49 ` [PATCH bpf-next v1 1/3] x86/mm: Resolve BPF exception fixups for user address faults " Kumar Kartikeya Dwivedi
2026-10-09 4:13 ` Borislav Petkov
2026-10-09 15:23 ` Kumar Kartikeya Dwivedi
2026-10-09 2:49 ` [PATCH bpf-next v1 2/3] bpf, x86: Skip the PROBE_MEM address range check " Kumar Kartikeya Dwivedi
2026-10-09 3:42 ` bot+bpf-ci
2026-10-09 2:49 ` Kumar Kartikeya Dwivedi [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=20261009024925.3169077-4-memxor@gmail.com \
--to=memxor@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bp@alien8.de \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=dave.hansen@linux.intel.com \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=ihor.solodrai@linux.dev \
--cc=kernel-team@meta.com \
--cc=kkd@meta.com \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@kernel.org \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=puranjay@kernel.org \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
/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
all inboxes | Powered by JetHome®