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
FIXTURE_SETUP(coredump)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
FIXTURE_TEARDOWN(coredump)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