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