[PATCH bpf-next 3/3] selftests/bpf: Add tests for bpf_kern_path kfunc
Song Liu
song at kernel.org
Thu Nov 27 00:50:11 UTC 2025
Add comprehensive selftests for the new bpf_kern_path and bpf_path_put
kfuncs:
1. Functional tests (prog_tests/kern_path.c, progs/test_kern_path.c):
- test_kern_path_basic: Tests successful path resolution using
/proc/self/exe and validates the resolved path with bpf_path_d_path
- test_kern_path_sb_mount: Tests bpf_kern_path with dynamic input
from LSM hook parameter (dev_name from sb_mount), demonstrating
real-world usage where BPF programs resolve paths from hook args
2. Verifier success tests (progs/verifier_kern_path.c):
- kern_path_success: Proper acquire -> use -> release pattern
- kern_path_multiple_paths: Multiple concurrent path acquisitions
3. Verifier failure tests (progs/verifier_kern_path_fail.c):
- kern_path_unreleased: Resource leak detection
- path_put_unacquired: Releasing unacquired path
- path_use_after_put: Use-after-free detection
- double_path_put: Double-free detection
- kern_path_non_lsm: Program type restrictions (LSM only)
- kern_path_non_const_str: reject none const string
These tests verify both the functionality of the kfuncs and that the
verifier properly enforces acquire/release semantics to prevent
resource leaks.
Signed-off-by: Song Liu <song at kernel.org>
---
.../testing/selftests/bpf/bpf_experimental.h | 4 +
.../selftests/bpf/prog_tests/kern_path.c | 82 ++++++++++++++++
.../selftests/bpf/progs/test_kern_path.c | 56 +++++++++++
.../selftests/bpf/progs/verifier_kern_path.c | 52 ++++++++++
.../bpf/progs/verifier_kern_path_fail.c | 97 +++++++++++++++++++
5 files changed, 291 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/kern_path.c
create mode 100644 tools/testing/selftests/bpf/progs/test_kern_path.c
create mode 100644 tools/testing/selftests/bpf/progs/verifier_kern_path.c
create mode 100644 tools/testing/selftests/bpf/progs/verifier_kern_path_fail.c
diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h
index 2cd9165c7348..c512c9a14752 100644
--- a/tools/testing/selftests/bpf/bpf_experimental.h
+++ b/tools/testing/selftests/bpf/bpf_experimental.h
@@ -221,6 +221,10 @@ extern void bpf_put_file(struct file *file) __ksym;
*/
extern int bpf_path_d_path(const struct path *path, char *buf, size_t buf__sz) __ksym;
+extern struct path *bpf_kern_path(const char *pathname, unsigned int flags) __ksym;
+extern void bpf_path_put(struct path *path) __ksym;
+extern int bpf_path_d_path(const struct path *path, char *buf, size_t buf__sz) __ksym;
+
/* This macro must be used to mark the exception callback corresponding to the
* main program. For example:
*
diff --git a/tools/testing/selftests/bpf/prog_tests/kern_path.c b/tools/testing/selftests/bpf/prog_tests/kern_path.c
new file mode 100644
index 000000000000..f4cdfe202a26
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kern_path.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. */
+
+#include <test_progs.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "test_kern_path.skel.h"
+#include "verifier_kern_path.skel.h"
+#include "verifier_kern_path_fail.skel.h"
+
+static void __test_kern_path(void (*trigger)(void))
+{
+ struct test_kern_path *skel;
+ int err;
+
+ skel = test_kern_path__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_kern_path__open_and_load"))
+ return;
+
+ skel->bss->monitored_pid = getpid();
+
+ err = test_kern_path__attach(skel);
+ if (!ASSERT_OK(err, "test_kern_path__attach"))
+ goto cleanup;
+
+ trigger();
+
+ /* Verify the bpf_path_d_path worked */
+ ASSERT_GT(skel->bss->path_len, 0, "path_len > 0");
+
+cleanup:
+ test_kern_path__destroy(skel);
+}
+
+static void trigger_file_open(void)
+{
+ int fd;
+
+ fd = open("/dev/null", O_RDONLY);
+ if (!ASSERT_OK_FD(fd, "open /dev/null"))
+ return;
+ close(fd);
+}
+
+static void trigger_sb_mount(void)
+{
+ char tmpdir[] = "/tmp/bpf_kern_path_test_XXXXXX";
+ int err;
+
+ if (!ASSERT_OK_PTR(mkdtemp(tmpdir), "mkdtemp"))
+ return;
+
+ err = mount("/tmp", tmpdir, NULL, MS_BIND, NULL);
+ if (!ASSERT_OK(err, "bind mount"))
+ goto rmdir;
+
+ umount(tmpdir);
+rmdir:
+ rmdir(tmpdir);
+}
+
+void test_kern_path(void)
+{
+ if (test__start_subtest("file_open"))
+ __test_kern_path(trigger_file_open);
+
+ if (test__start_subtest("sb_mount"))
+ __test_kern_path(trigger_sb_mount);
+}
+
+void test_verifier_kern_path(void)
+{
+ RUN_TESTS(verifier_kern_path);
+}
+
+void test_verifier_kern_path_fail(void)
+{
+ RUN_TESTS(verifier_kern_path_fail);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_kern_path.c b/tools/testing/selftests/bpf/progs/test_kern_path.c
new file mode 100644
index 000000000000..e9186a1aa990
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_kern_path.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+#include "bpf_experimental.h"
+
+#define MAX_PATH_LEN 256
+
+char buf[MAX_PATH_LEN];
+int path_len = 0;
+u32 monitored_pid = 0;
+
+SEC("lsm.s/file_open")
+int BPF_PROG(test_kern_path_basic, struct file *file)
+{
+ struct path *p;
+ int ret;
+
+ if (bpf_get_current_pid_tgid() >> 32 != monitored_pid)
+ return 0;
+
+ p = bpf_kern_path("/proc/self/exe", 0);
+ if (p) {
+ ret = bpf_path_d_path(p, buf, MAX_PATH_LEN);
+ if (ret > 0)
+ path_len = ret;
+ bpf_path_put(p);
+ }
+
+ return 0;
+}
+
+SEC("lsm.s/sb_mount")
+int BPF_PROG(test_kern_path_from_sb_mount, const char *dev_name, const struct path *path,
+ const char *type, unsigned long flags, void *data)
+{
+ struct path *p;
+ int ret;
+
+ if (bpf_get_current_pid_tgid() >> 32 != monitored_pid)
+ return 0;
+
+ p = bpf_kern_path(dev_name, 0);
+ if (p) {
+ ret = bpf_path_d_path(p, buf, MAX_PATH_LEN);
+ if (ret > 0)
+ path_len = ret;
+ bpf_path_put(p);
+ }
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_kern_path.c b/tools/testing/selftests/bpf/progs/verifier_kern_path.c
new file mode 100644
index 000000000000..0e6ccf640b64
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_kern_path.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <linux/limits.h>
+#include "bpf_misc.h"
+#include "bpf_experimental.h"
+
+static char buf[PATH_MAX];
+
+SEC("lsm.s/file_open")
+__success
+int BPF_PROG(kern_path_success)
+{
+ struct path *p;
+
+ p = bpf_kern_path("/proc/self/exe", 0);
+ if (!p)
+ return 0;
+
+ bpf_path_d_path(p, buf, sizeof(buf));
+
+ bpf_path_put(p);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__success
+int BPF_PROG(kern_path_multiple_paths)
+{
+ struct path *p1, *p2;
+
+ p1 = bpf_kern_path("/proc/self/exe", 0);
+ if (!p1)
+ return 0;
+
+ p2 = bpf_kern_path("/proc/self/cwd", 0);
+ if (!p2) {
+ bpf_path_put(p1);
+ return 0;
+ }
+
+ bpf_path_d_path(p1, buf, sizeof(buf));
+ bpf_path_d_path(p2, buf, sizeof(buf));
+
+ bpf_path_put(p2);
+ bpf_path_put(p1);
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_kern_path_fail.c b/tools/testing/selftests/bpf/progs/verifier_kern_path_fail.c
new file mode 100644
index 000000000000..520c227af5ca
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_kern_path_fail.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <linux/limits.h>
+#include "bpf_misc.h"
+#include "bpf_experimental.h"
+
+static char buf[PATH_MAX];
+
+SEC("lsm.s/file_open")
+__failure __msg("Unreleased reference")
+int BPF_PROG(kern_path_unreleased)
+{
+ struct path *p;
+
+ p = bpf_kern_path("/proc/self/exe", 0);
+ if (!p)
+ return 0;
+
+ /* Acquired but never released - should fail verification */
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("pointer type STRUCT path must point to scalar, or struct with scalar")
+int BPF_PROG(path_put_unacquired)
+{
+ struct path p = {};
+
+ /* Can't release an unacquired path - should fail verification */
+ bpf_path_put(&p);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("pointer type STRUCT path must point to scalar, or struct with scalar")
+int BPF_PROG(path_use_after_put, struct file *file)
+{
+ struct path *p;
+
+ p = bpf_kern_path("/proc/self/exe", 0);
+ if (!p)
+ return 0;
+
+ bpf_path_put(p);
+
+ /* Using path after put - should fail verification */
+ bpf_path_d_path(p, buf, sizeof(buf));
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("pointer type STRUCT path must point to scalar, or struct with scalar")
+int BPF_PROG(double_path_put)
+{
+ struct path *p;
+
+ p = bpf_kern_path("/proc/self/exe", 0);
+ if (!p)
+ return 0;
+
+ bpf_path_put(p);
+ /* Double put - should fail verification */
+ bpf_path_put(p);
+ return 0;
+}
+
+SEC("fentry/vfs_open")
+__failure __msg("calling kernel function bpf_kern_path is not allowed")
+int BPF_PROG(kern_path_non_lsm)
+{
+ struct path *p;
+
+ /* Calling bpf_kern_path() from a non-LSM BPF program isn't permitted */
+ p = bpf_kern_path("/proc/self/exe", 0);
+ if (p)
+ bpf_path_put(p);
+ return 0;
+}
+
+SEC("lsm.s/sb_eat_lsm_opts")
+__failure __msg("arg#0 doesn't point to a const string")
+int BPF_PROG(kern_path_non_const_str, char *options, void **mnt_opts)
+{
+ struct path *p;
+
+ /* Calling bpf_kern_path() from a with non-const string isn't permitted */
+ p = bpf_kern_path(options, 0);
+ if (p)
+ bpf_path_put(p);
+ return 0;
+}
+
+
+char _license[] SEC("license") = "GPL";
--
2.47.3
More information about the Linux-security-module-archive
mailing list