Skip to content

Commit 5f7b35f

Browse files
liu-song-6Kernel Patches Daemon
authored andcommitted
selftests/bpf: Add tests for bpf_kern_path kfunc
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 <[email protected]>
1 parent 3aecda0 commit 5f7b35f

File tree

5 files changed

+291
-0
lines changed

5 files changed

+291
-0
lines changed

tools/testing/selftests/bpf/bpf_experimental.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ extern void bpf_put_file(struct file *file) __ksym;
221221
*/
222222
extern int bpf_path_d_path(const struct path *path, char *buf, size_t buf__sz) __ksym;
223223

224+
extern struct path *bpf_kern_path(const char *pathname, unsigned int flags) __ksym;
225+
extern void bpf_path_put(struct path *path) __ksym;
226+
extern int bpf_path_d_path(const struct path *path, char *buf, size_t buf__sz) __ksym;
227+
224228
/* This macro must be used to mark the exception callback corresponding to the
225229
* main program. For example:
226230
*
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. */
3+
4+
#include <test_progs.h>
5+
#include <sys/mount.h>
6+
#include <fcntl.h>
7+
#include <unistd.h>
8+
#include <errno.h>
9+
10+
#include "test_kern_path.skel.h"
11+
#include "verifier_kern_path.skel.h"
12+
#include "verifier_kern_path_fail.skel.h"
13+
14+
static void __test_kern_path(void (*trigger)(void))
15+
{
16+
struct test_kern_path *skel;
17+
int err;
18+
19+
skel = test_kern_path__open_and_load();
20+
if (!ASSERT_OK_PTR(skel, "test_kern_path__open_and_load"))
21+
return;
22+
23+
skel->bss->monitored_pid = getpid();
24+
25+
err = test_kern_path__attach(skel);
26+
if (!ASSERT_OK(err, "test_kern_path__attach"))
27+
goto cleanup;
28+
29+
trigger();
30+
31+
/* Verify the bpf_path_d_path worked */
32+
ASSERT_GT(skel->bss->path_len, 0, "path_len > 0");
33+
34+
cleanup:
35+
test_kern_path__destroy(skel);
36+
}
37+
38+
static void trigger_file_open(void)
39+
{
40+
int fd;
41+
42+
fd = open("/dev/null", O_RDONLY);
43+
if (!ASSERT_OK_FD(fd, "open /dev/null"))
44+
return;
45+
close(fd);
46+
}
47+
48+
static void trigger_sb_mount(void)
49+
{
50+
char tmpdir[] = "/tmp/bpf_kern_path_test_XXXXXX";
51+
int err;
52+
53+
if (!ASSERT_OK_PTR(mkdtemp(tmpdir), "mkdtemp"))
54+
return;
55+
56+
err = mount("/tmp", tmpdir, NULL, MS_BIND, NULL);
57+
if (!ASSERT_OK(err, "bind mount"))
58+
goto rmdir;
59+
60+
umount(tmpdir);
61+
rmdir:
62+
rmdir(tmpdir);
63+
}
64+
65+
void test_kern_path(void)
66+
{
67+
if (test__start_subtest("file_open"))
68+
__test_kern_path(trigger_file_open);
69+
70+
if (test__start_subtest("sb_mount"))
71+
__test_kern_path(trigger_sb_mount);
72+
}
73+
74+
void test_verifier_kern_path(void)
75+
{
76+
RUN_TESTS(verifier_kern_path);
77+
}
78+
79+
void test_verifier_kern_path_fail(void)
80+
{
81+
RUN_TESTS(verifier_kern_path_fail);
82+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. */
3+
4+
#include "vmlinux.h"
5+
#include <bpf/bpf_tracing.h>
6+
#include "bpf_misc.h"
7+
#include "bpf_experimental.h"
8+
9+
#define MAX_PATH_LEN 256
10+
11+
char buf[MAX_PATH_LEN];
12+
int path_len = 0;
13+
u32 monitored_pid = 0;
14+
15+
SEC("lsm.s/file_open")
16+
int BPF_PROG(test_kern_path_basic, struct file *file)
17+
{
18+
struct path *p;
19+
int ret;
20+
21+
if (bpf_get_current_pid_tgid() >> 32 != monitored_pid)
22+
return 0;
23+
24+
p = bpf_kern_path("/proc/self/exe", 0);
25+
if (p) {
26+
ret = bpf_path_d_path(p, buf, MAX_PATH_LEN);
27+
if (ret > 0)
28+
path_len = ret;
29+
bpf_path_put(p);
30+
}
31+
32+
return 0;
33+
}
34+
35+
SEC("lsm.s/sb_mount")
36+
int BPF_PROG(test_kern_path_from_sb_mount, const char *dev_name, const struct path *path,
37+
const char *type, unsigned long flags, void *data)
38+
{
39+
struct path *p;
40+
int ret;
41+
42+
if (bpf_get_current_pid_tgid() >> 32 != monitored_pid)
43+
return 0;
44+
45+
p = bpf_kern_path(dev_name, 0);
46+
if (p) {
47+
ret = bpf_path_d_path(p, buf, MAX_PATH_LEN);
48+
if (ret > 0)
49+
path_len = ret;
50+
bpf_path_put(p);
51+
}
52+
53+
return 0;
54+
}
55+
56+
char _license[] SEC("license") = "GPL";
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. */
3+
4+
#include <vmlinux.h>
5+
#include <bpf/bpf_tracing.h>
6+
#include <linux/limits.h>
7+
#include "bpf_misc.h"
8+
#include "bpf_experimental.h"
9+
10+
static char buf[PATH_MAX];
11+
12+
SEC("lsm.s/file_open")
13+
__success
14+
int BPF_PROG(kern_path_success)
15+
{
16+
struct path *p;
17+
18+
p = bpf_kern_path("/proc/self/exe", 0);
19+
if (!p)
20+
return 0;
21+
22+
bpf_path_d_path(p, buf, sizeof(buf));
23+
24+
bpf_path_put(p);
25+
return 0;
26+
}
27+
28+
SEC("lsm.s/file_open")
29+
__success
30+
int BPF_PROG(kern_path_multiple_paths)
31+
{
32+
struct path *p1, *p2;
33+
34+
p1 = bpf_kern_path("/proc/self/exe", 0);
35+
if (!p1)
36+
return 0;
37+
38+
p2 = bpf_kern_path("/proc/self/cwd", 0);
39+
if (!p2) {
40+
bpf_path_put(p1);
41+
return 0;
42+
}
43+
44+
bpf_path_d_path(p1, buf, sizeof(buf));
45+
bpf_path_d_path(p2, buf, sizeof(buf));
46+
47+
bpf_path_put(p2);
48+
bpf_path_put(p1);
49+
return 0;
50+
}
51+
52+
char _license[] SEC("license") = "GPL";
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. */
3+
4+
#include <vmlinux.h>
5+
#include <bpf/bpf_tracing.h>
6+
#include <linux/limits.h>
7+
#include "bpf_misc.h"
8+
#include "bpf_experimental.h"
9+
10+
static char buf[PATH_MAX];
11+
12+
SEC("lsm.s/file_open")
13+
__failure __msg("Unreleased reference")
14+
int BPF_PROG(kern_path_unreleased)
15+
{
16+
struct path *p;
17+
18+
p = bpf_kern_path("/proc/self/exe", 0);
19+
if (!p)
20+
return 0;
21+
22+
/* Acquired but never released - should fail verification */
23+
return 0;
24+
}
25+
26+
SEC("lsm.s/file_open")
27+
__failure __msg("pointer type STRUCT path must point to scalar, or struct with scalar")
28+
int BPF_PROG(path_put_unacquired)
29+
{
30+
struct path p = {};
31+
32+
/* Can't release an unacquired path - should fail verification */
33+
bpf_path_put(&p);
34+
return 0;
35+
}
36+
37+
SEC("lsm.s/file_open")
38+
__failure __msg("pointer type STRUCT path must point to scalar, or struct with scalar")
39+
int BPF_PROG(path_use_after_put, struct file *file)
40+
{
41+
struct path *p;
42+
43+
p = bpf_kern_path("/proc/self/exe", 0);
44+
if (!p)
45+
return 0;
46+
47+
bpf_path_put(p);
48+
49+
/* Using path after put - should fail verification */
50+
bpf_path_d_path(p, buf, sizeof(buf));
51+
return 0;
52+
}
53+
54+
SEC("lsm.s/file_open")
55+
__failure __msg("pointer type STRUCT path must point to scalar, or struct with scalar")
56+
int BPF_PROG(double_path_put)
57+
{
58+
struct path *p;
59+
60+
p = bpf_kern_path("/proc/self/exe", 0);
61+
if (!p)
62+
return 0;
63+
64+
bpf_path_put(p);
65+
/* Double put - should fail verification */
66+
bpf_path_put(p);
67+
return 0;
68+
}
69+
70+
SEC("fentry/vfs_open")
71+
__failure __msg("calling kernel function bpf_kern_path is not allowed")
72+
int BPF_PROG(kern_path_non_lsm)
73+
{
74+
struct path *p;
75+
76+
/* Calling bpf_kern_path() from a non-LSM BPF program isn't permitted */
77+
p = bpf_kern_path("/proc/self/exe", 0);
78+
if (p)
79+
bpf_path_put(p);
80+
return 0;
81+
}
82+
83+
SEC("lsm.s/sb_eat_lsm_opts")
84+
__failure __msg("arg#0 doesn't point to a const string")
85+
int BPF_PROG(kern_path_non_const_str, char *options, void **mnt_opts)
86+
{
87+
struct path *p;
88+
89+
/* Calling bpf_kern_path() from a with non-const string isn't permitted */
90+
p = bpf_kern_path(options, 0);
91+
if (p)
92+
bpf_path_put(p);
93+
return 0;
94+
}
95+
96+
97+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)