Skip to content

Commit e4d1af4

Browse files
olsajiriAlexei Starovoitov
authored andcommitted
selftests/bpf: Add test for d_path helper
Adding test for d_path helper which is pretty much copied from Wenbo Zhang's test for bpf_get_fd_path, which never made it in. The test is doing fstat/close on several fd types, and verifies we got the d_path helper working on kernel probes for vfs_getattr/filp_close functions. Original-patch-by: Wenbo Zhang <[email protected]> Signed-off-by: Jiri Olsa <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 762f851 commit e4d1af4

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#define _GNU_SOURCE
3+
#include <test_progs.h>
4+
#include <sys/stat.h>
5+
#include <linux/sched.h>
6+
#include <sys/syscall.h>
7+
8+
#define MAX_PATH_LEN 128
9+
#define MAX_FILES 7
10+
11+
#include "test_d_path.skel.h"
12+
13+
static int duration;
14+
15+
static struct {
16+
__u32 cnt;
17+
char paths[MAX_FILES][MAX_PATH_LEN];
18+
} src;
19+
20+
static int set_pathname(int fd, pid_t pid)
21+
{
22+
char buf[MAX_PATH_LEN];
23+
24+
snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", pid, fd);
25+
return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
26+
}
27+
28+
static int trigger_fstat_events(pid_t pid)
29+
{
30+
int sockfd = -1, procfd = -1, devfd = -1;
31+
int localfd = -1, indicatorfd = -1;
32+
int pipefd[2] = { -1, -1 };
33+
struct stat fileStat;
34+
int ret = -1;
35+
36+
/* unmountable pseudo-filesystems */
37+
if (CHECK(pipe(pipefd) < 0, "trigger", "pipe failed\n"))
38+
return ret;
39+
/* unmountable pseudo-filesystems */
40+
sockfd = socket(AF_INET, SOCK_STREAM, 0);
41+
if (CHECK(sockfd < 0, "trigger", "scoket failed\n"))
42+
goto out_close;
43+
/* mountable pseudo-filesystems */
44+
procfd = open("/proc/self/comm", O_RDONLY);
45+
if (CHECK(procfd < 0, "trigger", "open /proc/self/comm failed\n"))
46+
goto out_close;
47+
devfd = open("/dev/urandom", O_RDONLY);
48+
if (CHECK(devfd < 0, "trigger", "open /dev/urandom failed\n"))
49+
goto out_close;
50+
localfd = open("/tmp/d_path_loadgen.txt", O_CREAT | O_RDONLY);
51+
if (CHECK(localfd < 0, "trigger", "open /tmp/d_path_loadgen.txt failed\n"))
52+
goto out_close;
53+
/* bpf_d_path will return path with (deleted) */
54+
remove("/tmp/d_path_loadgen.txt");
55+
indicatorfd = open("/tmp/", O_PATH);
56+
if (CHECK(indicatorfd < 0, "trigger", "open /tmp/ failed\n"))
57+
goto out_close;
58+
59+
ret = set_pathname(pipefd[0], pid);
60+
if (CHECK(ret < 0, "trigger", "set_pathname failed for pipe[0]\n"))
61+
goto out_close;
62+
ret = set_pathname(pipefd[1], pid);
63+
if (CHECK(ret < 0, "trigger", "set_pathname failed for pipe[1]\n"))
64+
goto out_close;
65+
ret = set_pathname(sockfd, pid);
66+
if (CHECK(ret < 0, "trigger", "set_pathname failed for socket\n"))
67+
goto out_close;
68+
ret = set_pathname(procfd, pid);
69+
if (CHECK(ret < 0, "trigger", "set_pathname failed for proc\n"))
70+
goto out_close;
71+
ret = set_pathname(devfd, pid);
72+
if (CHECK(ret < 0, "trigger", "set_pathname failed for dev\n"))
73+
goto out_close;
74+
ret = set_pathname(localfd, pid);
75+
if (CHECK(ret < 0, "trigger", "set_pathname failed for file\n"))
76+
goto out_close;
77+
ret = set_pathname(indicatorfd, pid);
78+
if (CHECK(ret < 0, "trigger", "set_pathname failed for dir\n"))
79+
goto out_close;
80+
81+
/* triggers vfs_getattr */
82+
fstat(pipefd[0], &fileStat);
83+
fstat(pipefd[1], &fileStat);
84+
fstat(sockfd, &fileStat);
85+
fstat(procfd, &fileStat);
86+
fstat(devfd, &fileStat);
87+
fstat(localfd, &fileStat);
88+
fstat(indicatorfd, &fileStat);
89+
90+
out_close:
91+
/* triggers filp_close */
92+
close(pipefd[0]);
93+
close(pipefd[1]);
94+
close(sockfd);
95+
close(procfd);
96+
close(devfd);
97+
close(localfd);
98+
close(indicatorfd);
99+
return ret;
100+
}
101+
102+
void test_d_path(void)
103+
{
104+
struct test_d_path__bss *bss;
105+
struct test_d_path *skel;
106+
int err;
107+
108+
skel = test_d_path__open_and_load();
109+
if (CHECK(!skel, "setup", "d_path skeleton failed\n"))
110+
goto cleanup;
111+
112+
err = test_d_path__attach(skel);
113+
if (CHECK(err, "setup", "attach failed: %d\n", err))
114+
goto cleanup;
115+
116+
bss = skel->bss;
117+
bss->my_pid = getpid();
118+
119+
err = trigger_fstat_events(bss->my_pid);
120+
if (err < 0)
121+
goto cleanup;
122+
123+
for (int i = 0; i < MAX_FILES; i++) {
124+
CHECK(strncmp(src.paths[i], bss->paths_stat[i], MAX_PATH_LEN),
125+
"check",
126+
"failed to get stat path[%d]: %s vs %s\n",
127+
i, src.paths[i], bss->paths_stat[i]);
128+
CHECK(strncmp(src.paths[i], bss->paths_close[i], MAX_PATH_LEN),
129+
"check",
130+
"failed to get close path[%d]: %s vs %s\n",
131+
i, src.paths[i], bss->paths_close[i]);
132+
/* The d_path helper returns size plus NUL char, hence + 1 */
133+
CHECK(bss->rets_stat[i] != strlen(bss->paths_stat[i]) + 1,
134+
"check",
135+
"failed to match stat return [%d]: %d vs %zd [%s]\n",
136+
i, bss->rets_stat[i], strlen(bss->paths_stat[i]) + 1,
137+
bss->paths_stat[i]);
138+
CHECK(bss->rets_close[i] != strlen(bss->paths_stat[i]) + 1,
139+
"check",
140+
"failed to match stat return [%d]: %d vs %zd [%s]\n",
141+
i, bss->rets_close[i], strlen(bss->paths_close[i]) + 1,
142+
bss->paths_stat[i]);
143+
}
144+
145+
cleanup:
146+
test_d_path__destroy(skel);
147+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
7+
#define MAX_PATH_LEN 128
8+
#define MAX_FILES 7
9+
10+
pid_t my_pid = 0;
11+
__u32 cnt_stat = 0;
12+
__u32 cnt_close = 0;
13+
char paths_stat[MAX_FILES][MAX_PATH_LEN] = {};
14+
char paths_close[MAX_FILES][MAX_PATH_LEN] = {};
15+
int rets_stat[MAX_FILES] = {};
16+
int rets_close[MAX_FILES] = {};
17+
18+
SEC("fentry/vfs_getattr")
19+
int BPF_PROG(prog_stat, struct path *path, struct kstat *stat,
20+
__u32 request_mask, unsigned int query_flags)
21+
{
22+
pid_t pid = bpf_get_current_pid_tgid() >> 32;
23+
__u32 cnt = cnt_stat;
24+
int ret;
25+
26+
if (pid != my_pid)
27+
return 0;
28+
29+
if (cnt >= MAX_FILES)
30+
return 0;
31+
ret = bpf_d_path(path, paths_stat[cnt], MAX_PATH_LEN);
32+
33+
rets_stat[cnt] = ret;
34+
cnt_stat++;
35+
return 0;
36+
}
37+
38+
SEC("fentry/filp_close")
39+
int BPF_PROG(prog_close, struct file *file, void *id)
40+
{
41+
pid_t pid = bpf_get_current_pid_tgid() >> 32;
42+
__u32 cnt = cnt_close;
43+
int ret;
44+
45+
if (pid != my_pid)
46+
return 0;
47+
48+
if (cnt >= MAX_FILES)
49+
return 0;
50+
ret = bpf_d_path(&file->f_path,
51+
paths_close[cnt], MAX_PATH_LEN);
52+
53+
rets_close[cnt] = ret;
54+
cnt_close++;
55+
return 0;
56+
}
57+
58+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)