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 #include "test_d_path_check_rdonly_mem.skel.h" 13 #include "test_d_path_check_types.skel.h" 14 15 /* sys_close_range is not around for long time, so let's 16 * make sure we can call it on systems with older glibc 17 */ 18 #ifndef __NR_close_range 19 #ifdef __alpha__ 20 #define __NR_close_range 546 21 #else 22 #define __NR_close_range 436 23 #endif 24 #endif 25 26 static int duration; 27 28 static struct { 29 __u32 cnt; 30 char paths[MAX_FILES][MAX_PATH_LEN]; 31 } src; 32 33 static int set_pathname(int fd, pid_t pid) 34 { 35 char buf[MAX_PATH_LEN]; 36 37 snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", pid, fd); 38 return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN); 39 } 40 41 static int trigger_fstat_events(pid_t pid) 42 { 43 int sockfd = -1, procfd = -1, devfd = -1; 44 int localfd = -1, indicatorfd = -1; 45 int pipefd[2] = { -1, -1 }; 46 struct stat fileStat; 47 int ret = -1; 48 49 /* unmountable pseudo-filesystems */ 50 if (CHECK(pipe(pipefd) < 0, "trigger", "pipe failed\n")) 51 return ret; 52 /* unmountable pseudo-filesystems */ 53 sockfd = socket(AF_INET, SOCK_STREAM, 0); 54 if (CHECK(sockfd < 0, "trigger", "socket failed\n")) 55 goto out_close; 56 /* mountable pseudo-filesystems */ 57 procfd = open("/proc/self/comm", O_RDONLY); 58 if (CHECK(procfd < 0, "trigger", "open /proc/self/comm failed\n")) 59 goto out_close; 60 devfd = open("/dev/urandom", O_RDONLY); 61 if (CHECK(devfd < 0, "trigger", "open /dev/urandom failed\n")) 62 goto out_close; 63 localfd = open("/tmp/d_path_loadgen.txt", O_CREAT | O_RDONLY, 0644); 64 if (CHECK(localfd < 0, "trigger", "open /tmp/d_path_loadgen.txt failed\n")) 65 goto out_close; 66 /* bpf_d_path will return path with (deleted) */ 67 remove("/tmp/d_path_loadgen.txt"); 68 indicatorfd = open("/tmp/", O_PATH); 69 if (CHECK(indicatorfd < 0, "trigger", "open /tmp/ failed\n")) 70 goto out_close; 71 72 ret = set_pathname(pipefd[0], pid); 73 if (CHECK(ret < 0, "trigger", "set_pathname failed for pipe[0]\n")) 74 goto out_close; 75 ret = set_pathname(pipefd[1], pid); 76 if (CHECK(ret < 0, "trigger", "set_pathname failed for pipe[1]\n")) 77 goto out_close; 78 ret = set_pathname(sockfd, pid); 79 if (CHECK(ret < 0, "trigger", "set_pathname failed for socket\n")) 80 goto out_close; 81 ret = set_pathname(procfd, pid); 82 if (CHECK(ret < 0, "trigger", "set_pathname failed for proc\n")) 83 goto out_close; 84 ret = set_pathname(devfd, pid); 85 if (CHECK(ret < 0, "trigger", "set_pathname failed for dev\n")) 86 goto out_close; 87 ret = set_pathname(localfd, pid); 88 if (CHECK(ret < 0, "trigger", "set_pathname failed for file\n")) 89 goto out_close; 90 ret = set_pathname(indicatorfd, pid); 91 if (CHECK(ret < 0, "trigger", "set_pathname failed for dir\n")) 92 goto out_close; 93 94 /* triggers vfs_getattr */ 95 fstat(pipefd[0], &fileStat); 96 fstat(pipefd[1], &fileStat); 97 fstat(sockfd, &fileStat); 98 fstat(procfd, &fileStat); 99 fstat(devfd, &fileStat); 100 fstat(localfd, &fileStat); 101 fstat(indicatorfd, &fileStat); 102 103 out_close: 104 /* sys_close no longer triggers filp_close, but we can 105 * call sys_close_range instead which still does 106 */ 107 #define close(fd) syscall(__NR_close_range, fd, fd, 0) 108 109 close(pipefd[0]); 110 close(pipefd[1]); 111 close(sockfd); 112 close(procfd); 113 close(devfd); 114 close(localfd); 115 close(indicatorfd); 116 117 #undef close 118 return ret; 119 } 120 121 static void test_d_path_basic(void) 122 { 123 struct test_d_path__bss *bss; 124 struct test_d_path *skel; 125 int err; 126 127 skel = test_d_path__open_and_load(); 128 if (CHECK(!skel, "setup", "d_path skeleton failed\n")) 129 goto cleanup; 130 131 err = test_d_path__attach(skel); 132 if (CHECK(err, "setup", "attach failed: %d\n", err)) 133 goto cleanup; 134 135 bss = skel->bss; 136 bss->my_pid = getpid(); 137 138 err = trigger_fstat_events(bss->my_pid); 139 if (err < 0) 140 goto cleanup; 141 142 if (CHECK(!bss->called_stat, 143 "stat", 144 "trampoline for security_inode_getattr was not called\n")) 145 goto cleanup; 146 147 if (CHECK(!bss->called_close, 148 "close", 149 "trampoline for filp_close was not called\n")) 150 goto cleanup; 151 152 for (int i = 0; i < MAX_FILES; i++) { 153 CHECK(strncmp(src.paths[i], bss->paths_stat[i], MAX_PATH_LEN), 154 "check", 155 "failed to get stat path[%d]: %s vs %s\n", 156 i, src.paths[i], bss->paths_stat[i]); 157 CHECK(strncmp(src.paths[i], bss->paths_close[i], MAX_PATH_LEN), 158 "check", 159 "failed to get close path[%d]: %s vs %s\n", 160 i, src.paths[i], bss->paths_close[i]); 161 /* The d_path helper returns size plus NUL char, hence + 1 */ 162 CHECK(bss->rets_stat[i] != strlen(bss->paths_stat[i]) + 1, 163 "check", 164 "failed to match stat return [%d]: %d vs %zd [%s]\n", 165 i, bss->rets_stat[i], strlen(bss->paths_stat[i]) + 1, 166 bss->paths_stat[i]); 167 CHECK(bss->rets_close[i] != strlen(bss->paths_stat[i]) + 1, 168 "check", 169 "failed to match stat return [%d]: %d vs %zd [%s]\n", 170 i, bss->rets_close[i], strlen(bss->paths_close[i]) + 1, 171 bss->paths_stat[i]); 172 } 173 174 cleanup: 175 test_d_path__destroy(skel); 176 } 177 178 static void test_d_path_check_rdonly_mem(void) 179 { 180 struct test_d_path_check_rdonly_mem *skel; 181 182 skel = test_d_path_check_rdonly_mem__open_and_load(); 183 ASSERT_ERR_PTR(skel, "unexpected_load_overwriting_rdonly_mem"); 184 185 test_d_path_check_rdonly_mem__destroy(skel); 186 } 187 188 static void test_d_path_check_types(void) 189 { 190 struct test_d_path_check_types *skel; 191 192 skel = test_d_path_check_types__open_and_load(); 193 ASSERT_ERR_PTR(skel, "unexpected_load_passing_wrong_type"); 194 195 test_d_path_check_types__destroy(skel); 196 } 197 198 void test_d_path(void) 199 { 200 if (test__start_subtest("basic")) 201 test_d_path_basic(); 202 203 if (test__start_subtest("check_rdonly_mem")) 204 test_d_path_check_rdonly_mem(); 205 206 if (test__start_subtest("check_alloc_mem")) 207 test_d_path_check_types(); 208 } 209