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", "socket 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, 0644); 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 if (CHECK(!bss->called_stat, 124 "stat", 125 "trampoline for security_inode_getattr was not called\n")) 126 goto cleanup; 127 128 if (CHECK(!bss->called_close, 129 "close", 130 "trampoline for filp_close was not called\n")) 131 goto cleanup; 132 133 for (int i = 0; i < MAX_FILES; i++) { 134 CHECK(strncmp(src.paths[i], bss->paths_stat[i], MAX_PATH_LEN), 135 "check", 136 "failed to get stat path[%d]: %s vs %s\n", 137 i, src.paths[i], bss->paths_stat[i]); 138 CHECK(strncmp(src.paths[i], bss->paths_close[i], MAX_PATH_LEN), 139 "check", 140 "failed to get close path[%d]: %s vs %s\n", 141 i, src.paths[i], bss->paths_close[i]); 142 /* The d_path helper returns size plus NUL char, hence + 1 */ 143 CHECK(bss->rets_stat[i] != strlen(bss->paths_stat[i]) + 1, 144 "check", 145 "failed to match stat return [%d]: %d vs %zd [%s]\n", 146 i, bss->rets_stat[i], strlen(bss->paths_stat[i]) + 1, 147 bss->paths_stat[i]); 148 CHECK(bss->rets_close[i] != strlen(bss->paths_stat[i]) + 1, 149 "check", 150 "failed to match stat return [%d]: %d vs %zd [%s]\n", 151 i, bss->rets_close[i], strlen(bss->paths_close[i]) + 1, 152 bss->paths_stat[i]); 153 } 154 155 cleanup: 156 test_d_path__destroy(skel); 157 } 158