[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:07 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