-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <ethercflow@gmail.com> Signed-off-by: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Andrii Nakryiko <andriin@fb.com> Link: https://lore.kernel.org/bpf/20200825192124.710397-14-jolsa@kernel.org
- Loading branch information
Jiri Olsa
authored and
Alexei Starovoitov
committed
Aug 25, 2020
1 parent
762f851
commit e4d1af4
Showing
2 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
#define _GNU_SOURCE | ||
#include <test_progs.h> | ||
#include <sys/stat.h> | ||
#include <linux/sched.h> | ||
#include <sys/syscall.h> | ||
|
||
#define MAX_PATH_LEN 128 | ||
#define MAX_FILES 7 | ||
|
||
#include "test_d_path.skel.h" | ||
|
||
static int duration; | ||
|
||
static struct { | ||
__u32 cnt; | ||
char paths[MAX_FILES][MAX_PATH_LEN]; | ||
} src; | ||
|
||
static int set_pathname(int fd, pid_t pid) | ||
{ | ||
char buf[MAX_PATH_LEN]; | ||
|
||
snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", pid, fd); | ||
return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN); | ||
} | ||
|
||
static int trigger_fstat_events(pid_t pid) | ||
{ | ||
int sockfd = -1, procfd = -1, devfd = -1; | ||
int localfd = -1, indicatorfd = -1; | ||
int pipefd[2] = { -1, -1 }; | ||
struct stat fileStat; | ||
int ret = -1; | ||
|
||
/* unmountable pseudo-filesystems */ | ||
if (CHECK(pipe(pipefd) < 0, "trigger", "pipe failed\n")) | ||
return ret; | ||
/* unmountable pseudo-filesystems */ | ||
sockfd = socket(AF_INET, SOCK_STREAM, 0); | ||
if (CHECK(sockfd < 0, "trigger", "scoket failed\n")) | ||
goto out_close; | ||
/* mountable pseudo-filesystems */ | ||
procfd = open("/proc/self/comm", O_RDONLY); | ||
if (CHECK(procfd < 0, "trigger", "open /proc/self/comm failed\n")) | ||
goto out_close; | ||
devfd = open("/dev/urandom", O_RDONLY); | ||
if (CHECK(devfd < 0, "trigger", "open /dev/urandom failed\n")) | ||
goto out_close; | ||
localfd = open("/tmp/d_path_loadgen.txt", O_CREAT | O_RDONLY); | ||
if (CHECK(localfd < 0, "trigger", "open /tmp/d_path_loadgen.txt failed\n")) | ||
goto out_close; | ||
/* bpf_d_path will return path with (deleted) */ | ||
remove("/tmp/d_path_loadgen.txt"); | ||
indicatorfd = open("/tmp/", O_PATH); | ||
if (CHECK(indicatorfd < 0, "trigger", "open /tmp/ failed\n")) | ||
goto out_close; | ||
|
||
ret = set_pathname(pipefd[0], pid); | ||
if (CHECK(ret < 0, "trigger", "set_pathname failed for pipe[0]\n")) | ||
goto out_close; | ||
ret = set_pathname(pipefd[1], pid); | ||
if (CHECK(ret < 0, "trigger", "set_pathname failed for pipe[1]\n")) | ||
goto out_close; | ||
ret = set_pathname(sockfd, pid); | ||
if (CHECK(ret < 0, "trigger", "set_pathname failed for socket\n")) | ||
goto out_close; | ||
ret = set_pathname(procfd, pid); | ||
if (CHECK(ret < 0, "trigger", "set_pathname failed for proc\n")) | ||
goto out_close; | ||
ret = set_pathname(devfd, pid); | ||
if (CHECK(ret < 0, "trigger", "set_pathname failed for dev\n")) | ||
goto out_close; | ||
ret = set_pathname(localfd, pid); | ||
if (CHECK(ret < 0, "trigger", "set_pathname failed for file\n")) | ||
goto out_close; | ||
ret = set_pathname(indicatorfd, pid); | ||
if (CHECK(ret < 0, "trigger", "set_pathname failed for dir\n")) | ||
goto out_close; | ||
|
||
/* triggers vfs_getattr */ | ||
fstat(pipefd[0], &fileStat); | ||
fstat(pipefd[1], &fileStat); | ||
fstat(sockfd, &fileStat); | ||
fstat(procfd, &fileStat); | ||
fstat(devfd, &fileStat); | ||
fstat(localfd, &fileStat); | ||
fstat(indicatorfd, &fileStat); | ||
|
||
out_close: | ||
/* triggers filp_close */ | ||
close(pipefd[0]); | ||
close(pipefd[1]); | ||
close(sockfd); | ||
close(procfd); | ||
close(devfd); | ||
close(localfd); | ||
close(indicatorfd); | ||
return ret; | ||
} | ||
|
||
void test_d_path(void) | ||
{ | ||
struct test_d_path__bss *bss; | ||
struct test_d_path *skel; | ||
int err; | ||
|
||
skel = test_d_path__open_and_load(); | ||
if (CHECK(!skel, "setup", "d_path skeleton failed\n")) | ||
goto cleanup; | ||
|
||
err = test_d_path__attach(skel); | ||
if (CHECK(err, "setup", "attach failed: %d\n", err)) | ||
goto cleanup; | ||
|
||
bss = skel->bss; | ||
bss->my_pid = getpid(); | ||
|
||
err = trigger_fstat_events(bss->my_pid); | ||
if (err < 0) | ||
goto cleanup; | ||
|
||
for (int i = 0; i < MAX_FILES; i++) { | ||
CHECK(strncmp(src.paths[i], bss->paths_stat[i], MAX_PATH_LEN), | ||
"check", | ||
"failed to get stat path[%d]: %s vs %s\n", | ||
i, src.paths[i], bss->paths_stat[i]); | ||
CHECK(strncmp(src.paths[i], bss->paths_close[i], MAX_PATH_LEN), | ||
"check", | ||
"failed to get close path[%d]: %s vs %s\n", | ||
i, src.paths[i], bss->paths_close[i]); | ||
/* The d_path helper returns size plus NUL char, hence + 1 */ | ||
CHECK(bss->rets_stat[i] != strlen(bss->paths_stat[i]) + 1, | ||
"check", | ||
"failed to match stat return [%d]: %d vs %zd [%s]\n", | ||
i, bss->rets_stat[i], strlen(bss->paths_stat[i]) + 1, | ||
bss->paths_stat[i]); | ||
CHECK(bss->rets_close[i] != strlen(bss->paths_stat[i]) + 1, | ||
"check", | ||
"failed to match stat return [%d]: %d vs %zd [%s]\n", | ||
i, bss->rets_close[i], strlen(bss->paths_close[i]) + 1, | ||
bss->paths_stat[i]); | ||
} | ||
|
||
cleanup: | ||
test_d_path__destroy(skel); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
#include "vmlinux.h" | ||
#include <bpf/bpf_helpers.h> | ||
#include <bpf/bpf_tracing.h> | ||
|
||
#define MAX_PATH_LEN 128 | ||
#define MAX_FILES 7 | ||
|
||
pid_t my_pid = 0; | ||
__u32 cnt_stat = 0; | ||
__u32 cnt_close = 0; | ||
char paths_stat[MAX_FILES][MAX_PATH_LEN] = {}; | ||
char paths_close[MAX_FILES][MAX_PATH_LEN] = {}; | ||
int rets_stat[MAX_FILES] = {}; | ||
int rets_close[MAX_FILES] = {}; | ||
|
||
SEC("fentry/vfs_getattr") | ||
int BPF_PROG(prog_stat, struct path *path, struct kstat *stat, | ||
__u32 request_mask, unsigned int query_flags) | ||
{ | ||
pid_t pid = bpf_get_current_pid_tgid() >> 32; | ||
__u32 cnt = cnt_stat; | ||
int ret; | ||
|
||
if (pid != my_pid) | ||
return 0; | ||
|
||
if (cnt >= MAX_FILES) | ||
return 0; | ||
ret = bpf_d_path(path, paths_stat[cnt], MAX_PATH_LEN); | ||
|
||
rets_stat[cnt] = ret; | ||
cnt_stat++; | ||
return 0; | ||
} | ||
|
||
SEC("fentry/filp_close") | ||
int BPF_PROG(prog_close, struct file *file, void *id) | ||
{ | ||
pid_t pid = bpf_get_current_pid_tgid() >> 32; | ||
__u32 cnt = cnt_close; | ||
int ret; | ||
|
||
if (pid != my_pid) | ||
return 0; | ||
|
||
if (cnt >= MAX_FILES) | ||
return 0; | ||
ret = bpf_d_path(&file->f_path, | ||
paths_close[cnt], MAX_PATH_LEN); | ||
|
||
rets_close[cnt] = ret; | ||
cnt_close++; | ||
return 0; | ||
} | ||
|
||
char _license[] SEC("license") = "GPL"; |