1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <assert.h> 4 #include <fcntl.h> 5 #include <inttypes.h> 6 #include <libgen.h> 7 #include <limits.h> 8 #include <linux/coredump.h> 9 #include <linux/fs.h> 10 #include <linux/limits.h> 11 #include <pthread.h> 12 #include <string.h> 13 #include <sys/mount.h> 14 #include <poll.h> 15 #include <sys/epoll.h> 16 #include <sys/resource.h> 17 #include <sys/stat.h> 18 #include <sys/socket.h> 19 #include <sys/un.h> 20 #include <unistd.h> 21 22 #include "../kselftest_harness.h" 23 #include "../filesystems/wrappers.h" 24 #include "../pidfd/pidfd.h" 25 26 #include "coredump_test.h" 27 28 #define STACKDUMP_FILE "stack_values" 29 #define STACKDUMP_SCRIPT "stackdump" 30 31 #ifndef PAGE_SIZE 32 #define PAGE_SIZE 4096 33 #endif 34 35 FIXTURE_SETUP(coredump) 36 { 37 FILE *file; 38 int ret; 39 40 self->pid_coredump_server = -ESRCH; 41 self->fd_tmpfs_detached = -1; 42 file = fopen("/proc/sys/kernel/core_pattern", "r"); 43 ASSERT_NE(NULL, file); 44 45 ret = fread(self->original_core_pattern, 1, sizeof(self->original_core_pattern), file); 46 ASSERT_TRUE(ret || feof(file)); 47 ASSERT_LT(ret, sizeof(self->original_core_pattern)); 48 49 self->original_core_pattern[ret] = '\0'; 50 self->fd_tmpfs_detached = create_detached_tmpfs(); 51 ASSERT_GE(self->fd_tmpfs_detached, 0); 52 53 ret = fclose(file); 54 ASSERT_EQ(0, ret); 55 } 56 57 FIXTURE_TEARDOWN(coredump) 58 { 59 const char *reason; 60 FILE *file; 61 int ret, status; 62 63 unlink(STACKDUMP_FILE); 64 65 if (self->pid_coredump_server > 0) { 66 kill(self->pid_coredump_server, SIGTERM); 67 waitpid(self->pid_coredump_server, &status, 0); 68 } 69 unlink("/tmp/coredump.file"); 70 unlink("/tmp/coredump.socket"); 71 72 file = fopen("/proc/sys/kernel/core_pattern", "w"); 73 if (!file) { 74 reason = "Unable to open core_pattern"; 75 goto fail; 76 } 77 78 ret = fprintf(file, "%s", self->original_core_pattern); 79 if (ret < 0) { 80 reason = "Unable to write to core_pattern"; 81 goto fail; 82 } 83 84 ret = fclose(file); 85 if (ret) { 86 reason = "Unable to close core_pattern"; 87 goto fail; 88 } 89 90 if (self->fd_tmpfs_detached >= 0) { 91 ret = close(self->fd_tmpfs_detached); 92 if (ret < 0) { 93 reason = "Unable to close detached tmpfs"; 94 goto fail; 95 } 96 self->fd_tmpfs_detached = -1; 97 } 98 99 return; 100 fail: 101 /* This should never happen */ 102 fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason); 103 } 104 105 TEST_F_TIMEOUT(coredump, stackdump, 120) 106 { 107 unsigned long long stack; 108 char *test_dir, *line; 109 size_t line_length; 110 char buf[PAGE_SIZE]; 111 int ret, i, status; 112 FILE *file; 113 pid_t pid; 114 115 /* 116 * Step 1: Setup core_pattern so that the stackdump script is executed when the child 117 * process crashes 118 */ 119 ret = readlink("/proc/self/exe", buf, sizeof(buf)); 120 ASSERT_NE(-1, ret); 121 ASSERT_LT(ret, sizeof(buf)); 122 buf[ret] = '\0'; 123 124 test_dir = dirname(buf); 125 126 file = fopen("/proc/sys/kernel/core_pattern", "w"); 127 ASSERT_NE(NULL, file); 128 129 ret = fprintf(file, "|%1$s/%2$s %%P %1$s/%3$s", test_dir, STACKDUMP_SCRIPT, STACKDUMP_FILE); 130 ASSERT_LT(0, ret); 131 132 ret = fclose(file); 133 ASSERT_EQ(0, ret); 134 135 /* Step 2: Create a process who spawns some threads then crashes */ 136 pid = fork(); 137 ASSERT_TRUE(pid >= 0); 138 if (pid == 0) 139 crashing_child(); 140 141 /* 142 * Step 3: Wait for the stackdump script to write the stack pointers to the stackdump file 143 */ 144 waitpid(pid, &status, 0); 145 ASSERT_TRUE(WIFSIGNALED(status)); 146 ASSERT_TRUE(WCOREDUMP(status)); 147 148 for (i = 0; i < 10; ++i) { 149 file = fopen(STACKDUMP_FILE, "r"); 150 if (file) 151 break; 152 sleep(1); 153 } 154 ASSERT_NE(file, NULL); 155 156 /* Step 4: Make sure all stack pointer values are non-zero */ 157 line = NULL; 158 for (i = 0; -1 != getline(&line, &line_length, file); ++i) { 159 stack = strtoull(line, NULL, 10); 160 ASSERT_NE(stack, 0); 161 } 162 free(line); 163 164 ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN); 165 166 fclose(file); 167 } 168 169 TEST_HARNESS_MAIN 170