|  | // 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" | 
|  | #include "test_d_path_check_rdonly_mem.skel.h" | 
|  | #include "test_d_path_check_types.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", "socket 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, 0644); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | static void test_d_path_basic(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; | 
|  |  | 
|  | if (CHECK(!bss->called_stat, | 
|  | "stat", | 
|  | "trampoline for security_inode_getattr was not called\n")) | 
|  | goto cleanup; | 
|  |  | 
|  | if (CHECK(!bss->called_close, | 
|  | "close", | 
|  | "trampoline for filp_close was not called\n")) | 
|  | 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); | 
|  | } | 
|  |  | 
|  | static void test_d_path_check_rdonly_mem(void) | 
|  | { | 
|  | struct test_d_path_check_rdonly_mem *skel; | 
|  |  | 
|  | skel = test_d_path_check_rdonly_mem__open_and_load(); | 
|  | ASSERT_ERR_PTR(skel, "unexpected_load_overwriting_rdonly_mem"); | 
|  |  | 
|  | test_d_path_check_rdonly_mem__destroy(skel); | 
|  | } | 
|  |  | 
|  | static void test_d_path_check_types(void) | 
|  | { | 
|  | struct test_d_path_check_types *skel; | 
|  |  | 
|  | skel = test_d_path_check_types__open_and_load(); | 
|  | ASSERT_ERR_PTR(skel, "unexpected_load_passing_wrong_type"); | 
|  |  | 
|  | test_d_path_check_types__destroy(skel); | 
|  | } | 
|  |  | 
|  | void test_d_path(void) | 
|  | { | 
|  | if (test__start_subtest("basic")) | 
|  | test_d_path_basic(); | 
|  |  | 
|  | if (test__start_subtest("check_rdonly_mem")) | 
|  | test_d_path_check_rdonly_mem(); | 
|  |  | 
|  | if (test__start_subtest("check_alloc_mem")) | 
|  | test_d_path_check_types(); | 
|  | } |