1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <assert.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <limits.h>
7 #include <linux/coredump.h>
8 #include <linux/fs.h>
9 #include <pthread.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/epoll.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <sys/un.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21
22 #include "../filesystems/wrappers.h"
23 #include "../pidfd/pidfd.h"
24
25 /* Forward declarations to avoid including harness header */
26 struct __test_metadata;
27
28 /* Match the fixture definition from coredump_test.h */
29 struct _fixture_coredump_data {
30 char original_core_pattern[256];
31 pid_t pid_coredump_server;
32 int fd_tmpfs_detached;
33 };
34
35 #ifndef PAGE_SIZE
36 #define PAGE_SIZE 4096
37 #endif
38
39 #define NUM_THREAD_SPAWN 128
40
do_nothing(void * arg)41 void *do_nothing(void *arg)
42 {
43 (void)arg;
44 while (1)
45 pause();
46
47 return NULL;
48 }
49
crashing_child(void)50 void crashing_child(void)
51 {
52 pthread_t thread;
53 int i;
54
55 for (i = 0; i < NUM_THREAD_SPAWN; ++i)
56 pthread_create(&thread, NULL, do_nothing, NULL);
57
58 /* crash on purpose */
59 i = *(int *)NULL;
60 }
61
create_detached_tmpfs(void)62 int create_detached_tmpfs(void)
63 {
64 int fd_context, fd_tmpfs;
65
66 fd_context = sys_fsopen("tmpfs", 0);
67 if (fd_context < 0)
68 return -1;
69
70 if (sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
71 return -1;
72
73 fd_tmpfs = sys_fsmount(fd_context, 0, 0);
74 close(fd_context);
75 return fd_tmpfs;
76 }
77
create_and_listen_unix_socket(const char * path)78 int create_and_listen_unix_socket(const char *path)
79 {
80 struct sockaddr_un addr = {
81 .sun_family = AF_UNIX,
82 };
83 assert(strlen(path) < sizeof(addr.sun_path) - 1);
84 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
85 size_t addr_len =
86 offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1;
87 int fd, ret;
88
89 fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
90 if (fd < 0)
91 goto out;
92
93 ret = bind(fd, (const struct sockaddr *)&addr, addr_len);
94 if (ret < 0)
95 goto out;
96
97 ret = listen(fd, 128);
98 if (ret < 0)
99 goto out;
100
101 return fd;
102
103 out:
104 if (fd >= 0)
105 close(fd);
106 return -1;
107 }
108
set_core_pattern(const char * pattern)109 bool set_core_pattern(const char *pattern)
110 {
111 int fd;
112 ssize_t ret;
113
114 fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC);
115 if (fd < 0)
116 return false;
117
118 ret = write(fd, pattern, strlen(pattern));
119 close(fd);
120 if (ret < 0)
121 return false;
122
123 fprintf(stderr, "Set core_pattern to '%s' | %zu == %zu\n", pattern, ret, strlen(pattern));
124 return ret == strlen(pattern);
125 }
126
get_peer_pidfd(int fd)127 int get_peer_pidfd(int fd)
128 {
129 int fd_peer_pidfd;
130 socklen_t fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
131 int ret = getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &fd_peer_pidfd,
132 &fd_peer_pidfd_len);
133 if (ret < 0) {
134 fprintf(stderr, "get_peer_pidfd: getsockopt(SO_PEERPIDFD) failed: %m\n");
135 return -1;
136 }
137 fprintf(stderr, "get_peer_pidfd: successfully retrieved pidfd %d\n", fd_peer_pidfd);
138 return fd_peer_pidfd;
139 }
140
get_pidfd_info(int fd_peer_pidfd,struct pidfd_info * info)141 bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info)
142 {
143 int ret;
144 memset(info, 0, sizeof(*info));
145 info->mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL;
146 ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, info);
147 if (ret < 0) {
148 fprintf(stderr, "get_pidfd_info: ioctl(PIDFD_GET_INFO) failed: %m\n");
149 return false;
150 }
151 fprintf(stderr, "get_pidfd_info: mask=0x%llx, coredump_mask=0x%x, coredump_signal=%d\n",
152 (unsigned long long)info->mask, info->coredump_mask, info->coredump_signal);
153 return true;
154 }
155
156 /* Protocol helper functions */
157
recv_marker(int fd)158 ssize_t recv_marker(int fd)
159 {
160 enum coredump_mark mark = COREDUMP_MARK_REQACK;
161 ssize_t ret;
162
163 ret = recv(fd, &mark, sizeof(mark), MSG_WAITALL);
164 if (ret != sizeof(mark))
165 return -1;
166
167 switch (mark) {
168 case COREDUMP_MARK_REQACK:
169 fprintf(stderr, "Received marker: ReqAck\n");
170 return COREDUMP_MARK_REQACK;
171 case COREDUMP_MARK_MINSIZE:
172 fprintf(stderr, "Received marker: MinSize\n");
173 return COREDUMP_MARK_MINSIZE;
174 case COREDUMP_MARK_MAXSIZE:
175 fprintf(stderr, "Received marker: MaxSize\n");
176 return COREDUMP_MARK_MAXSIZE;
177 case COREDUMP_MARK_UNSUPPORTED:
178 fprintf(stderr, "Received marker: Unsupported\n");
179 return COREDUMP_MARK_UNSUPPORTED;
180 case COREDUMP_MARK_CONFLICTING:
181 fprintf(stderr, "Received marker: Conflicting\n");
182 return COREDUMP_MARK_CONFLICTING;
183 default:
184 fprintf(stderr, "Received unknown marker: %u\n", mark);
185 break;
186 }
187 return -1;
188 }
189
read_marker(int fd,enum coredump_mark mark)190 bool read_marker(int fd, enum coredump_mark mark)
191 {
192 ssize_t ret;
193
194 ret = recv_marker(fd);
195 if (ret < 0)
196 return false;
197 return ret == mark;
198 }
199
read_coredump_req(int fd,struct coredump_req * req)200 bool read_coredump_req(int fd, struct coredump_req *req)
201 {
202 ssize_t ret;
203 size_t field_size, user_size, ack_size, kernel_size, remaining_size;
204
205 memset(req, 0, sizeof(*req));
206 field_size = sizeof(req->size);
207
208 /* Peek the size of the coredump request. */
209 ret = recv(fd, req, field_size, MSG_PEEK | MSG_WAITALL);
210 if (ret != field_size) {
211 fprintf(stderr, "read_coredump_req: peek failed (got %zd, expected %zu): %m\n",
212 ret, field_size);
213 return false;
214 }
215 kernel_size = req->size;
216
217 if (kernel_size < COREDUMP_ACK_SIZE_VER0) {
218 fprintf(stderr, "read_coredump_req: kernel_size %zu < min %d\n",
219 kernel_size, COREDUMP_ACK_SIZE_VER0);
220 return false;
221 }
222 if (kernel_size >= PAGE_SIZE) {
223 fprintf(stderr, "read_coredump_req: kernel_size %zu >= PAGE_SIZE %d\n",
224 kernel_size, PAGE_SIZE);
225 return false;
226 }
227
228 /* Use the minimum of user and kernel size to read the full request. */
229 user_size = sizeof(struct coredump_req);
230 ack_size = user_size < kernel_size ? user_size : kernel_size;
231 ret = recv(fd, req, ack_size, MSG_WAITALL);
232 if (ret != ack_size)
233 return false;
234
235 fprintf(stderr, "Read coredump request with size %u and mask 0x%llx\n",
236 req->size, (unsigned long long)req->mask);
237
238 if (user_size > kernel_size)
239 remaining_size = user_size - kernel_size;
240 else
241 remaining_size = kernel_size - user_size;
242
243 if (PAGE_SIZE <= remaining_size)
244 return false;
245
246 /*
247 * Discard any additional data if the kernel's request was larger than
248 * what we knew about or cared about.
249 */
250 if (remaining_size) {
251 char buffer[PAGE_SIZE];
252
253 ret = recv(fd, buffer, sizeof(buffer), MSG_WAITALL);
254 if (ret != remaining_size)
255 return false;
256 fprintf(stderr, "Discarded %zu bytes of data after coredump request\n", remaining_size);
257 }
258
259 return true;
260 }
261
send_coredump_ack(int fd,const struct coredump_req * req,__u64 mask,size_t size_ack)262 bool send_coredump_ack(int fd, const struct coredump_req *req,
263 __u64 mask, size_t size_ack)
264 {
265 ssize_t ret;
266 /*
267 * Wrap struct coredump_ack in a larger struct so we can
268 * simulate sending to much data to the kernel.
269 */
270 struct large_ack_for_size_testing {
271 struct coredump_ack ack;
272 char buffer[PAGE_SIZE];
273 } large_ack = {};
274
275 if (!size_ack)
276 size_ack = sizeof(struct coredump_ack) < req->size_ack ?
277 sizeof(struct coredump_ack) :
278 req->size_ack;
279 large_ack.ack.mask = mask;
280 large_ack.ack.size = size_ack;
281 ret = send(fd, &large_ack, size_ack, MSG_NOSIGNAL);
282 if (ret != size_ack)
283 return false;
284
285 fprintf(stderr, "Sent coredump ack with size %zu and mask 0x%llx\n",
286 size_ack, (unsigned long long)mask);
287 return true;
288 }
289
check_coredump_req(const struct coredump_req * req,size_t min_size,__u64 required_mask)290 bool check_coredump_req(const struct coredump_req *req, size_t min_size,
291 __u64 required_mask)
292 {
293 if (req->size < min_size)
294 return false;
295 if ((req->mask & required_mask) != required_mask)
296 return false;
297 if (req->mask & ~required_mask)
298 return false;
299 return true;
300 }
301
open_coredump_tmpfile(int fd_tmpfs_detached)302 int open_coredump_tmpfile(int fd_tmpfs_detached)
303 {
304 return openat(fd_tmpfs_detached, ".", O_TMPFILE | O_RDWR | O_EXCL, 0600);
305 }
306
process_coredump_worker(int fd_coredump,int fd_peer_pidfd,int fd_core_file)307 void process_coredump_worker(int fd_coredump, int fd_peer_pidfd, int fd_core_file)
308 {
309 int epfd = -1;
310 int exit_code = EXIT_FAILURE;
311 struct epoll_event ev;
312 int flags;
313
314 /* Set socket to non-blocking mode for edge-triggered epoll */
315 flags = fcntl(fd_coredump, F_GETFL, 0);
316 if (flags < 0) {
317 fprintf(stderr, "Worker: fcntl(F_GETFL) failed: %m\n");
318 goto out;
319 }
320 if (fcntl(fd_coredump, F_SETFL, flags | O_NONBLOCK) < 0) {
321 fprintf(stderr, "Worker: fcntl(F_SETFL, O_NONBLOCK) failed: %m\n");
322 goto out;
323 }
324
325 epfd = epoll_create1(0);
326 if (epfd < 0) {
327 fprintf(stderr, "Worker: epoll_create1() failed: %m\n");
328 goto out;
329 }
330
331 ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
332 ev.data.fd = fd_coredump;
333 if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd_coredump, &ev) < 0) {
334 fprintf(stderr, "Worker: epoll_ctl(EPOLL_CTL_ADD) failed: %m\n");
335 goto out;
336 }
337
338 for (;;) {
339 struct epoll_event events[1];
340 int n = epoll_wait(epfd, events, 1, -1);
341 if (n < 0) {
342 fprintf(stderr, "Worker: epoll_wait() failed: %m\n");
343 break;
344 }
345
346 if (events[0].events & (EPOLLIN | EPOLLRDHUP)) {
347 for (;;) {
348 char buffer[4096];
349 ssize_t bytes_read = read(fd_coredump, buffer, sizeof(buffer));
350 if (bytes_read < 0) {
351 if (errno == EAGAIN || errno == EWOULDBLOCK)
352 break;
353 fprintf(stderr, "Worker: read() failed: %m\n");
354 goto out;
355 }
356 if (bytes_read == 0)
357 goto done;
358 ssize_t bytes_write = write(fd_core_file, buffer, bytes_read);
359 if (bytes_write != bytes_read) {
360 if (bytes_write < 0 && errno == ENOSPC)
361 continue;
362 fprintf(stderr, "Worker: write() failed (read=%zd, write=%zd): %m\n",
363 bytes_read, bytes_write);
364 goto out;
365 }
366 }
367 }
368 }
369
370 done:
371 exit_code = EXIT_SUCCESS;
372 fprintf(stderr, "Worker: completed successfully\n");
373 out:
374 if (epfd >= 0)
375 close(epfd);
376 if (fd_core_file >= 0)
377 close(fd_core_file);
378 if (fd_peer_pidfd >= 0)
379 close(fd_peer_pidfd);
380 if (fd_coredump >= 0)
381 close(fd_coredump);
382 _exit(exit_code);
383 }
384