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