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 #define STACKDUMP_FILE "stack_values"
27 #define STACKDUMP_SCRIPT "stackdump"
28 #define NUM_THREAD_SPAWN 128
29
30 #ifndef PAGE_SIZE
31 #define PAGE_SIZE 4096
32 #endif
33
do_nothing(void *)34 static void *do_nothing(void *)
35 {
36 while (1)
37 pause();
38
39 return NULL;
40 }
41
crashing_child(void)42 static void crashing_child(void)
43 {
44 pthread_t thread;
45 int i;
46
47 for (i = 0; i < NUM_THREAD_SPAWN; ++i)
48 pthread_create(&thread, NULL, do_nothing, NULL);
49
50 /* crash on purpose */
51 i = *(int *)NULL;
52 }
53
FIXTURE(coredump)54 FIXTURE(coredump)
55 {
56 char original_core_pattern[256];
57 pid_t pid_coredump_server;
58 int fd_tmpfs_detached;
59 };
60
create_detached_tmpfs(void)61 static int create_detached_tmpfs(void)
62 {
63 int fd_context, fd_tmpfs;
64
65 fd_context = sys_fsopen("tmpfs", 0);
66 if (fd_context < 0)
67 return -1;
68
69 if (sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
70 return -1;
71
72 fd_tmpfs = sys_fsmount(fd_context, 0, 0);
73 close(fd_context);
74 return fd_tmpfs;
75 }
76
FIXTURE_SETUP(coredump)77 FIXTURE_SETUP(coredump)
78 {
79 FILE *file;
80 int ret;
81
82 self->pid_coredump_server = -ESRCH;
83 self->fd_tmpfs_detached = -1;
84 file = fopen("/proc/sys/kernel/core_pattern", "r");
85 ASSERT_NE(NULL, file);
86
87 ret = fread(self->original_core_pattern, 1, sizeof(self->original_core_pattern), file);
88 ASSERT_TRUE(ret || feof(file));
89 ASSERT_LT(ret, sizeof(self->original_core_pattern));
90
91 self->original_core_pattern[ret] = '\0';
92 self->fd_tmpfs_detached = create_detached_tmpfs();
93 ASSERT_GE(self->fd_tmpfs_detached, 0);
94
95 ret = fclose(file);
96 ASSERT_EQ(0, ret);
97 }
98
FIXTURE_TEARDOWN(coredump)99 FIXTURE_TEARDOWN(coredump)
100 {
101 const char *reason;
102 FILE *file;
103 int ret, status;
104
105 unlink(STACKDUMP_FILE);
106
107 if (self->pid_coredump_server > 0) {
108 kill(self->pid_coredump_server, SIGTERM);
109 waitpid(self->pid_coredump_server, &status, 0);
110 }
111 unlink("/tmp/coredump.file");
112 unlink("/tmp/coredump.socket");
113
114 file = fopen("/proc/sys/kernel/core_pattern", "w");
115 if (!file) {
116 reason = "Unable to open core_pattern";
117 goto fail;
118 }
119
120 ret = fprintf(file, "%s", self->original_core_pattern);
121 if (ret < 0) {
122 reason = "Unable to write to core_pattern";
123 goto fail;
124 }
125
126 ret = fclose(file);
127 if (ret) {
128 reason = "Unable to close core_pattern";
129 goto fail;
130 }
131
132 if (self->fd_tmpfs_detached >= 0) {
133 ret = close(self->fd_tmpfs_detached);
134 if (ret < 0) {
135 reason = "Unable to close detached tmpfs";
136 goto fail;
137 }
138 self->fd_tmpfs_detached = -1;
139 }
140
141 return;
142 fail:
143 /* This should never happen */
144 fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason);
145 }
146
147 TEST_F_TIMEOUT(coredump, stackdump, 120)
148 {
149 unsigned long long stack;
150 char *test_dir, *line;
151 size_t line_length;
152 char buf[PAGE_SIZE];
153 int ret, i, status;
154 FILE *file;
155 pid_t pid;
156
157 /*
158 * Step 1: Setup core_pattern so that the stackdump script is executed when the child
159 * process crashes
160 */
161 ret = readlink("/proc/self/exe", buf, sizeof(buf));
162 ASSERT_NE(-1, ret);
163 ASSERT_LT(ret, sizeof(buf));
164 buf[ret] = '\0';
165
166 test_dir = dirname(buf);
167
168 file = fopen("/proc/sys/kernel/core_pattern", "w");
169 ASSERT_NE(NULL, file);
170
171 ret = fprintf(file, "|%1$s/%2$s %%P %1$s/%3$s", test_dir, STACKDUMP_SCRIPT, STACKDUMP_FILE);
172 ASSERT_LT(0, ret);
173
174 ret = fclose(file);
175 ASSERT_EQ(0, ret);
176
177 /* Step 2: Create a process who spawns some threads then crashes */
178 pid = fork();
179 ASSERT_TRUE(pid >= 0);
180 if (pid == 0)
181 crashing_child();
182
183 /*
184 * Step 3: Wait for the stackdump script to write the stack pointers to the stackdump file
185 */
186 waitpid(pid, &status, 0);
187 ASSERT_TRUE(WIFSIGNALED(status));
188 ASSERT_TRUE(WCOREDUMP(status));
189
190 for (i = 0; i < 10; ++i) {
191 file = fopen(STACKDUMP_FILE, "r");
192 if (file)
193 break;
194 sleep(1);
195 }
196 ASSERT_NE(file, NULL);
197
198 /* Step 4: Make sure all stack pointer values are non-zero */
199 line = NULL;
200 for (i = 0; -1 != getline(&line, &line_length, file); ++i) {
201 stack = strtoull(line, NULL, 10);
202 ASSERT_NE(stack, 0);
203 }
204 free(line);
205
206 ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN);
207
208 fclose(file);
209 }
210
create_and_listen_unix_socket(const char * path)211 static int create_and_listen_unix_socket(const char *path)
212 {
213 struct sockaddr_un addr = {
214 .sun_family = AF_UNIX,
215 };
216 assert(strlen(path) < sizeof(addr.sun_path) - 1);
217 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
218 size_t addr_len =
219 offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1;
220 int fd, ret;
221
222 fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
223 if (fd < 0)
224 goto out;
225
226 ret = bind(fd, (const struct sockaddr *)&addr, addr_len);
227 if (ret < 0)
228 goto out;
229
230 ret = listen(fd, 128);
231 if (ret < 0)
232 goto out;
233
234 return fd;
235
236 out:
237 if (fd >= 0)
238 close(fd);
239 return -1;
240 }
241
set_core_pattern(const char * pattern)242 static bool set_core_pattern(const char *pattern)
243 {
244 int fd;
245 ssize_t ret;
246
247 fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC);
248 if (fd < 0)
249 return false;
250
251 ret = write(fd, pattern, strlen(pattern));
252 close(fd);
253 if (ret < 0)
254 return false;
255
256 fprintf(stderr, "Set core_pattern to '%s' | %zu == %zu\n", pattern, ret, strlen(pattern));
257 return ret == strlen(pattern);
258 }
259
get_peer_pidfd(int fd)260 static int get_peer_pidfd(int fd)
261 {
262 int fd_peer_pidfd;
263 socklen_t fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
264 int ret = getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &fd_peer_pidfd,
265 &fd_peer_pidfd_len);
266 if (ret < 0) {
267 fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n");
268 return -1;
269 }
270 return fd_peer_pidfd;
271 }
272
get_pidfd_info(int fd_peer_pidfd,struct pidfd_info * info)273 static bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info)
274 {
275 memset(info, 0, sizeof(*info));
276 info->mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
277 return ioctl(fd_peer_pidfd, PIDFD_GET_INFO, info) == 0;
278 }
279
280 static void
wait_and_check_coredump_server(pid_t pid_coredump_server,struct __test_metadata * const _metadata,FIXTURE_DATA (coredump)* self)281 wait_and_check_coredump_server(pid_t pid_coredump_server,
282 struct __test_metadata *const _metadata,
283 FIXTURE_DATA(coredump)* self)
284 {
285 int status;
286 waitpid(pid_coredump_server, &status, 0);
287 self->pid_coredump_server = -ESRCH;
288 ASSERT_TRUE(WIFEXITED(status));
289 ASSERT_EQ(WEXITSTATUS(status), 0);
290 }
291
TEST_F(coredump,socket)292 TEST_F(coredump, socket)
293 {
294 int pidfd, ret, status;
295 pid_t pid, pid_coredump_server;
296 struct stat st;
297 struct pidfd_info info = {};
298 int ipc_sockets[2];
299 char c;
300
301 ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket"));
302
303 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
304 ASSERT_EQ(ret, 0);
305
306 pid_coredump_server = fork();
307 ASSERT_GE(pid_coredump_server, 0);
308 if (pid_coredump_server == 0) {
309 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1;
310 int exit_code = EXIT_FAILURE;
311
312 close(ipc_sockets[0]);
313
314 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
315 if (fd_server < 0)
316 goto out;
317
318 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
319 goto out;
320
321 close(ipc_sockets[1]);
322
323 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
324 if (fd_coredump < 0)
325 goto out;
326
327 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
328 if (fd_peer_pidfd < 0)
329 goto out;
330
331 if (!get_pidfd_info(fd_peer_pidfd, &info))
332 goto out;
333
334 if (!(info.mask & PIDFD_INFO_COREDUMP))
335 goto out;
336
337 if (!(info.coredump_mask & PIDFD_COREDUMPED))
338 goto out;
339
340 fd_core_file = creat("/tmp/coredump.file", 0644);
341 if (fd_core_file < 0)
342 goto out;
343
344 for (;;) {
345 char buffer[4096];
346 ssize_t bytes_read, bytes_write;
347
348 bytes_read = read(fd_coredump, buffer, sizeof(buffer));
349 if (bytes_read < 0)
350 goto out;
351
352 if (bytes_read == 0)
353 break;
354
355 bytes_write = write(fd_core_file, buffer, bytes_read);
356 if (bytes_read != bytes_write)
357 goto out;
358 }
359
360 exit_code = EXIT_SUCCESS;
361 out:
362 if (fd_core_file >= 0)
363 close(fd_core_file);
364 if (fd_peer_pidfd >= 0)
365 close(fd_peer_pidfd);
366 if (fd_coredump >= 0)
367 close(fd_coredump);
368 if (fd_server >= 0)
369 close(fd_server);
370 _exit(exit_code);
371 }
372 self->pid_coredump_server = pid_coredump_server;
373
374 EXPECT_EQ(close(ipc_sockets[1]), 0);
375 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
376 EXPECT_EQ(close(ipc_sockets[0]), 0);
377
378 pid = fork();
379 ASSERT_GE(pid, 0);
380 if (pid == 0)
381 crashing_child();
382
383 pidfd = sys_pidfd_open(pid, 0);
384 ASSERT_GE(pidfd, 0);
385
386 waitpid(pid, &status, 0);
387 ASSERT_TRUE(WIFSIGNALED(status));
388 ASSERT_TRUE(WCOREDUMP(status));
389
390 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
391 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
392 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
393
394 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
395
396 ASSERT_EQ(stat("/tmp/coredump.file", &st), 0);
397 ASSERT_GT(st.st_size, 0);
398 system("file /tmp/coredump.file");
399 }
400
TEST_F(coredump,socket_detect_userspace_client)401 TEST_F(coredump, socket_detect_userspace_client)
402 {
403 int pidfd, ret, status;
404 pid_t pid, pid_coredump_server;
405 struct stat st;
406 struct pidfd_info info = {};
407 int ipc_sockets[2];
408 char c;
409
410 ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket"));
411
412 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
413 ASSERT_EQ(ret, 0);
414
415 pid_coredump_server = fork();
416 ASSERT_GE(pid_coredump_server, 0);
417 if (pid_coredump_server == 0) {
418 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
419 int exit_code = EXIT_FAILURE;
420
421 close(ipc_sockets[0]);
422
423 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
424 if (fd_server < 0)
425 goto out;
426
427 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
428 goto out;
429
430 close(ipc_sockets[1]);
431
432 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
433 if (fd_coredump < 0)
434 goto out;
435
436 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
437 if (fd_peer_pidfd < 0)
438 goto out;
439
440 if (!get_pidfd_info(fd_peer_pidfd, &info))
441 goto out;
442
443 if (!(info.mask & PIDFD_INFO_COREDUMP))
444 goto out;
445
446 if (info.coredump_mask & PIDFD_COREDUMPED)
447 goto out;
448
449 exit_code = EXIT_SUCCESS;
450 out:
451 if (fd_peer_pidfd >= 0)
452 close(fd_peer_pidfd);
453 if (fd_coredump >= 0)
454 close(fd_coredump);
455 if (fd_server >= 0)
456 close(fd_server);
457 _exit(exit_code);
458 }
459 self->pid_coredump_server = pid_coredump_server;
460
461 EXPECT_EQ(close(ipc_sockets[1]), 0);
462 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
463 EXPECT_EQ(close(ipc_sockets[0]), 0);
464
465 pid = fork();
466 ASSERT_GE(pid, 0);
467 if (pid == 0) {
468 int fd_socket;
469 ssize_t ret;
470 const struct sockaddr_un coredump_sk = {
471 .sun_family = AF_UNIX,
472 .sun_path = "/tmp/coredump.socket",
473 };
474 size_t coredump_sk_len =
475 offsetof(struct sockaddr_un, sun_path) +
476 sizeof("/tmp/coredump.socket");
477
478 fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
479 if (fd_socket < 0)
480 _exit(EXIT_FAILURE);
481
482 ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
483 if (ret < 0)
484 _exit(EXIT_FAILURE);
485
486 close(fd_socket);
487 _exit(EXIT_SUCCESS);
488 }
489
490 pidfd = sys_pidfd_open(pid, 0);
491 ASSERT_GE(pidfd, 0);
492
493 waitpid(pid, &status, 0);
494 ASSERT_TRUE(WIFEXITED(status));
495 ASSERT_EQ(WEXITSTATUS(status), 0);
496
497 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
498 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
499 ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0);
500
501 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
502
503 ASSERT_NE(stat("/tmp/coredump.file", &st), 0);
504 ASSERT_EQ(errno, ENOENT);
505 }
506
TEST_F(coredump,socket_enoent)507 TEST_F(coredump, socket_enoent)
508 {
509 int pidfd, status;
510 pid_t pid;
511
512 ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket"));
513
514 pid = fork();
515 ASSERT_GE(pid, 0);
516 if (pid == 0)
517 crashing_child();
518
519 pidfd = sys_pidfd_open(pid, 0);
520 ASSERT_GE(pidfd, 0);
521
522 waitpid(pid, &status, 0);
523 ASSERT_TRUE(WIFSIGNALED(status));
524 ASSERT_FALSE(WCOREDUMP(status));
525 }
526
TEST_F(coredump,socket_no_listener)527 TEST_F(coredump, socket_no_listener)
528 {
529 int pidfd, ret, status;
530 pid_t pid, pid_coredump_server;
531 int ipc_sockets[2];
532 char c;
533 const struct sockaddr_un coredump_sk = {
534 .sun_family = AF_UNIX,
535 .sun_path = "/tmp/coredump.socket",
536 };
537 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
538 sizeof("/tmp/coredump.socket");
539
540 ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket"));
541
542 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
543 ASSERT_EQ(ret, 0);
544
545 pid_coredump_server = fork();
546 ASSERT_GE(pid_coredump_server, 0);
547 if (pid_coredump_server == 0) {
548 int fd_server = -1;
549 int exit_code = EXIT_FAILURE;
550
551 close(ipc_sockets[0]);
552
553 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
554 if (fd_server < 0)
555 goto out;
556
557 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
558 if (ret < 0)
559 goto out;
560
561 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
562 goto out;
563
564 exit_code = EXIT_SUCCESS;
565 out:
566 if (fd_server >= 0)
567 close(fd_server);
568 close(ipc_sockets[1]);
569 _exit(exit_code);
570 }
571 self->pid_coredump_server = pid_coredump_server;
572
573 EXPECT_EQ(close(ipc_sockets[1]), 0);
574 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
575 EXPECT_EQ(close(ipc_sockets[0]), 0);
576
577 pid = fork();
578 ASSERT_GE(pid, 0);
579 if (pid == 0)
580 crashing_child();
581
582 pidfd = sys_pidfd_open(pid, 0);
583 ASSERT_GE(pidfd, 0);
584
585 waitpid(pid, &status, 0);
586 ASSERT_TRUE(WIFSIGNALED(status));
587 ASSERT_FALSE(WCOREDUMP(status));
588
589 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
590 }
591
recv_marker(int fd)592 static ssize_t recv_marker(int fd)
593 {
594 enum coredump_mark mark = COREDUMP_MARK_REQACK;
595 ssize_t ret;
596
597 ret = recv(fd, &mark, sizeof(mark), MSG_WAITALL);
598 if (ret != sizeof(mark))
599 return -1;
600
601 switch (mark) {
602 case COREDUMP_MARK_REQACK:
603 fprintf(stderr, "Received marker: ReqAck\n");
604 return COREDUMP_MARK_REQACK;
605 case COREDUMP_MARK_MINSIZE:
606 fprintf(stderr, "Received marker: MinSize\n");
607 return COREDUMP_MARK_MINSIZE;
608 case COREDUMP_MARK_MAXSIZE:
609 fprintf(stderr, "Received marker: MaxSize\n");
610 return COREDUMP_MARK_MAXSIZE;
611 case COREDUMP_MARK_UNSUPPORTED:
612 fprintf(stderr, "Received marker: Unsupported\n");
613 return COREDUMP_MARK_UNSUPPORTED;
614 case COREDUMP_MARK_CONFLICTING:
615 fprintf(stderr, "Received marker: Conflicting\n");
616 return COREDUMP_MARK_CONFLICTING;
617 default:
618 fprintf(stderr, "Received unknown marker: %u\n", mark);
619 break;
620 }
621 return -1;
622 }
623
read_marker(int fd,enum coredump_mark mark)624 static bool read_marker(int fd, enum coredump_mark mark)
625 {
626 ssize_t ret;
627
628 ret = recv_marker(fd);
629 if (ret < 0)
630 return false;
631 return ret == mark;
632 }
633
read_coredump_req(int fd,struct coredump_req * req)634 static bool read_coredump_req(int fd, struct coredump_req *req)
635 {
636 ssize_t ret;
637 size_t field_size, user_size, ack_size, kernel_size, remaining_size;
638
639 memset(req, 0, sizeof(*req));
640 field_size = sizeof(req->size);
641
642 /* Peek the size of the coredump request. */
643 ret = recv(fd, req, field_size, MSG_PEEK | MSG_WAITALL);
644 if (ret != field_size)
645 return false;
646 kernel_size = req->size;
647
648 if (kernel_size < COREDUMP_ACK_SIZE_VER0)
649 return false;
650 if (kernel_size >= PAGE_SIZE)
651 return false;
652
653 /* Use the minimum of user and kernel size to read the full request. */
654 user_size = sizeof(struct coredump_req);
655 ack_size = user_size < kernel_size ? user_size : kernel_size;
656 ret = recv(fd, req, ack_size, MSG_WAITALL);
657 if (ret != ack_size)
658 return false;
659
660 fprintf(stderr, "Read coredump request with size %u and mask 0x%llx\n",
661 req->size, (unsigned long long)req->mask);
662
663 if (user_size > kernel_size)
664 remaining_size = user_size - kernel_size;
665 else
666 remaining_size = kernel_size - user_size;
667
668 if (PAGE_SIZE <= remaining_size)
669 return false;
670
671 /*
672 * Discard any additional data if the kernel's request was larger than
673 * what we knew about or cared about.
674 */
675 if (remaining_size) {
676 char buffer[PAGE_SIZE];
677
678 ret = recv(fd, buffer, sizeof(buffer), MSG_WAITALL);
679 if (ret != remaining_size)
680 return false;
681 fprintf(stderr, "Discarded %zu bytes of data after coredump request\n", remaining_size);
682 }
683
684 return true;
685 }
686
send_coredump_ack(int fd,const struct coredump_req * req,__u64 mask,size_t size_ack)687 static bool send_coredump_ack(int fd, const struct coredump_req *req,
688 __u64 mask, size_t size_ack)
689 {
690 ssize_t ret;
691 /*
692 * Wrap struct coredump_ack in a larger struct so we can
693 * simulate sending to much data to the kernel.
694 */
695 struct large_ack_for_size_testing {
696 struct coredump_ack ack;
697 char buffer[PAGE_SIZE];
698 } large_ack = {};
699
700 if (!size_ack)
701 size_ack = sizeof(struct coredump_ack) < req->size_ack ?
702 sizeof(struct coredump_ack) :
703 req->size_ack;
704 large_ack.ack.mask = mask;
705 large_ack.ack.size = size_ack;
706 ret = send(fd, &large_ack, size_ack, MSG_NOSIGNAL);
707 if (ret != size_ack)
708 return false;
709
710 fprintf(stderr, "Sent coredump ack with size %zu and mask 0x%llx\n",
711 size_ack, (unsigned long long)mask);
712 return true;
713 }
714
check_coredump_req(const struct coredump_req * req,size_t min_size,__u64 required_mask)715 static bool check_coredump_req(const struct coredump_req *req, size_t min_size,
716 __u64 required_mask)
717 {
718 if (req->size < min_size)
719 return false;
720 if ((req->mask & required_mask) != required_mask)
721 return false;
722 if (req->mask & ~required_mask)
723 return false;
724 return true;
725 }
726
TEST_F(coredump,socket_request_kernel)727 TEST_F(coredump, socket_request_kernel)
728 {
729 int pidfd, ret, status;
730 pid_t pid, pid_coredump_server;
731 struct stat st;
732 struct pidfd_info info = {};
733 int ipc_sockets[2];
734 char c;
735
736 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
737
738 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
739 ASSERT_EQ(ret, 0);
740
741 pid_coredump_server = fork();
742 ASSERT_GE(pid_coredump_server, 0);
743 if (pid_coredump_server == 0) {
744 struct coredump_req req = {};
745 int fd_server = -1, fd_coredump = -1, fd_core_file = -1, fd_peer_pidfd = -1;
746 int exit_code = EXIT_FAILURE;
747
748 close(ipc_sockets[0]);
749
750 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
751 if (fd_server < 0)
752 goto out;
753
754 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
755 goto out;
756
757 close(ipc_sockets[1]);
758
759 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
760 if (fd_coredump < 0)
761 goto out;
762
763 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
764 if (fd_peer_pidfd < 0)
765 goto out;
766
767 if (!get_pidfd_info(fd_peer_pidfd, &info))
768 goto out;
769
770 if (!(info.mask & PIDFD_INFO_COREDUMP))
771 goto out;
772
773 if (!(info.coredump_mask & PIDFD_COREDUMPED))
774 goto out;
775
776 fd_core_file = creat("/tmp/coredump.file", 0644);
777 if (fd_core_file < 0)
778 goto out;
779
780 if (!read_coredump_req(fd_coredump, &req))
781 goto out;
782
783 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
784 COREDUMP_KERNEL | COREDUMP_USERSPACE |
785 COREDUMP_REJECT | COREDUMP_WAIT))
786 goto out;
787
788 if (!send_coredump_ack(fd_coredump, &req,
789 COREDUMP_KERNEL | COREDUMP_WAIT, 0))
790 goto out;
791
792 if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK))
793 goto out;
794
795 for (;;) {
796 char buffer[4096];
797 ssize_t bytes_read, bytes_write;
798
799 bytes_read = read(fd_coredump, buffer, sizeof(buffer));
800 if (bytes_read < 0)
801 goto out;
802
803 if (bytes_read == 0)
804 break;
805
806 bytes_write = write(fd_core_file, buffer, bytes_read);
807 if (bytes_read != bytes_write)
808 goto out;
809 }
810
811 exit_code = EXIT_SUCCESS;
812 out:
813 if (fd_core_file >= 0)
814 close(fd_core_file);
815 if (fd_peer_pidfd >= 0)
816 close(fd_peer_pidfd);
817 if (fd_coredump >= 0)
818 close(fd_coredump);
819 if (fd_server >= 0)
820 close(fd_server);
821 _exit(exit_code);
822 }
823 self->pid_coredump_server = pid_coredump_server;
824
825 EXPECT_EQ(close(ipc_sockets[1]), 0);
826 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
827 EXPECT_EQ(close(ipc_sockets[0]), 0);
828
829 pid = fork();
830 ASSERT_GE(pid, 0);
831 if (pid == 0)
832 crashing_child();
833
834 pidfd = sys_pidfd_open(pid, 0);
835 ASSERT_GE(pidfd, 0);
836
837 waitpid(pid, &status, 0);
838 ASSERT_TRUE(WIFSIGNALED(status));
839 ASSERT_TRUE(WCOREDUMP(status));
840
841 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
842 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
843 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
844
845 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
846
847 ASSERT_EQ(stat("/tmp/coredump.file", &st), 0);
848 ASSERT_GT(st.st_size, 0);
849 system("file /tmp/coredump.file");
850 }
851
TEST_F(coredump,socket_request_userspace)852 TEST_F(coredump, socket_request_userspace)
853 {
854 int pidfd, ret, status;
855 pid_t pid, pid_coredump_server;
856 struct pidfd_info info = {};
857 int ipc_sockets[2];
858 char c;
859
860 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
861
862 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
863 ASSERT_EQ(ret, 0);
864
865 pid_coredump_server = fork();
866 ASSERT_GE(pid_coredump_server, 0);
867 if (pid_coredump_server == 0) {
868 struct coredump_req req = {};
869 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
870 int exit_code = EXIT_FAILURE;
871
872 close(ipc_sockets[0]);
873
874 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
875 if (fd_server < 0)
876 goto out;
877
878 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
879 goto out;
880
881 close(ipc_sockets[1]);
882
883 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
884 if (fd_coredump < 0)
885 goto out;
886
887 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
888 if (fd_peer_pidfd < 0)
889 goto out;
890
891 if (!get_pidfd_info(fd_peer_pidfd, &info))
892 goto out;
893
894 if (!(info.mask & PIDFD_INFO_COREDUMP))
895 goto out;
896
897 if (!(info.coredump_mask & PIDFD_COREDUMPED))
898 goto out;
899
900 if (!read_coredump_req(fd_coredump, &req))
901 goto out;
902
903 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
904 COREDUMP_KERNEL | COREDUMP_USERSPACE |
905 COREDUMP_REJECT | COREDUMP_WAIT))
906 goto out;
907
908 if (!send_coredump_ack(fd_coredump, &req,
909 COREDUMP_USERSPACE | COREDUMP_WAIT, 0))
910 goto out;
911
912 if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK))
913 goto out;
914
915 for (;;) {
916 char buffer[4096];
917 ssize_t bytes_read;
918
919 bytes_read = read(fd_coredump, buffer, sizeof(buffer));
920 if (bytes_read > 0)
921 goto out;
922
923 if (bytes_read < 0)
924 goto out;
925
926 if (bytes_read == 0)
927 break;
928 }
929
930 exit_code = EXIT_SUCCESS;
931 out:
932 if (fd_peer_pidfd >= 0)
933 close(fd_peer_pidfd);
934 if (fd_coredump >= 0)
935 close(fd_coredump);
936 if (fd_server >= 0)
937 close(fd_server);
938 _exit(exit_code);
939 }
940 self->pid_coredump_server = pid_coredump_server;
941
942 EXPECT_EQ(close(ipc_sockets[1]), 0);
943 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
944 EXPECT_EQ(close(ipc_sockets[0]), 0);
945
946 pid = fork();
947 ASSERT_GE(pid, 0);
948 if (pid == 0)
949 crashing_child();
950
951 pidfd = sys_pidfd_open(pid, 0);
952 ASSERT_GE(pidfd, 0);
953
954 waitpid(pid, &status, 0);
955 ASSERT_TRUE(WIFSIGNALED(status));
956 ASSERT_TRUE(WCOREDUMP(status));
957
958 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
959 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
960 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
961
962 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
963 }
964
TEST_F(coredump,socket_request_reject)965 TEST_F(coredump, socket_request_reject)
966 {
967 int pidfd, ret, status;
968 pid_t pid, pid_coredump_server;
969 struct pidfd_info info = {};
970 int ipc_sockets[2];
971 char c;
972
973 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
974
975 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
976 ASSERT_EQ(ret, 0);
977
978 pid_coredump_server = fork();
979 ASSERT_GE(pid_coredump_server, 0);
980 if (pid_coredump_server == 0) {
981 struct coredump_req req = {};
982 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
983 int exit_code = EXIT_FAILURE;
984
985 close(ipc_sockets[0]);
986
987 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
988 if (fd_server < 0)
989 goto out;
990
991 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
992 goto out;
993
994 close(ipc_sockets[1]);
995
996 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
997 if (fd_coredump < 0)
998 goto out;
999
1000 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
1001 if (fd_peer_pidfd < 0)
1002 goto out;
1003
1004 if (!get_pidfd_info(fd_peer_pidfd, &info))
1005 goto out;
1006
1007 if (!(info.mask & PIDFD_INFO_COREDUMP))
1008 goto out;
1009
1010 if (!(info.coredump_mask & PIDFD_COREDUMPED))
1011 goto out;
1012
1013 if (!read_coredump_req(fd_coredump, &req))
1014 goto out;
1015
1016 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
1017 COREDUMP_KERNEL | COREDUMP_USERSPACE |
1018 COREDUMP_REJECT | COREDUMP_WAIT))
1019 goto out;
1020
1021 if (!send_coredump_ack(fd_coredump, &req,
1022 COREDUMP_REJECT | COREDUMP_WAIT, 0))
1023 goto out;
1024
1025 if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK))
1026 goto out;
1027
1028 for (;;) {
1029 char buffer[4096];
1030 ssize_t bytes_read;
1031
1032 bytes_read = read(fd_coredump, buffer, sizeof(buffer));
1033 if (bytes_read > 0)
1034 goto out;
1035
1036 if (bytes_read < 0)
1037 goto out;
1038
1039 if (bytes_read == 0)
1040 break;
1041 }
1042
1043 exit_code = EXIT_SUCCESS;
1044 out:
1045 if (fd_peer_pidfd >= 0)
1046 close(fd_peer_pidfd);
1047 if (fd_coredump >= 0)
1048 close(fd_coredump);
1049 if (fd_server >= 0)
1050 close(fd_server);
1051 _exit(exit_code);
1052 }
1053 self->pid_coredump_server = pid_coredump_server;
1054
1055 EXPECT_EQ(close(ipc_sockets[1]), 0);
1056 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
1057 EXPECT_EQ(close(ipc_sockets[0]), 0);
1058
1059 pid = fork();
1060 ASSERT_GE(pid, 0);
1061 if (pid == 0)
1062 crashing_child();
1063
1064 pidfd = sys_pidfd_open(pid, 0);
1065 ASSERT_GE(pidfd, 0);
1066
1067 waitpid(pid, &status, 0);
1068 ASSERT_TRUE(WIFSIGNALED(status));
1069 ASSERT_FALSE(WCOREDUMP(status));
1070
1071 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
1072 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
1073 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
1074
1075 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
1076 }
1077
TEST_F(coredump,socket_request_invalid_flag_combination)1078 TEST_F(coredump, socket_request_invalid_flag_combination)
1079 {
1080 int pidfd, ret, status;
1081 pid_t pid, pid_coredump_server;
1082 struct pidfd_info info = {};
1083 int ipc_sockets[2];
1084 char c;
1085
1086 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
1087
1088 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
1089 ASSERT_EQ(ret, 0);
1090
1091 pid_coredump_server = fork();
1092 ASSERT_GE(pid_coredump_server, 0);
1093 if (pid_coredump_server == 0) {
1094 struct coredump_req req = {};
1095 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
1096 int exit_code = EXIT_FAILURE;
1097
1098 close(ipc_sockets[0]);
1099
1100 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
1101 if (fd_server < 0)
1102 goto out;
1103
1104 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
1105 goto out;
1106
1107 close(ipc_sockets[1]);
1108
1109 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
1110 if (fd_coredump < 0)
1111 goto out;
1112
1113 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
1114 if (fd_peer_pidfd < 0)
1115 goto out;
1116
1117 if (!get_pidfd_info(fd_peer_pidfd, &info))
1118 goto out;
1119
1120 if (!(info.mask & PIDFD_INFO_COREDUMP))
1121 goto out;
1122
1123 if (!(info.coredump_mask & PIDFD_COREDUMPED))
1124 goto out;
1125
1126 if (!read_coredump_req(fd_coredump, &req))
1127 goto out;
1128
1129 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
1130 COREDUMP_KERNEL | COREDUMP_USERSPACE |
1131 COREDUMP_REJECT | COREDUMP_WAIT))
1132 goto out;
1133
1134 if (!send_coredump_ack(fd_coredump, &req,
1135 COREDUMP_KERNEL | COREDUMP_REJECT | COREDUMP_WAIT, 0))
1136 goto out;
1137
1138 if (!read_marker(fd_coredump, COREDUMP_MARK_CONFLICTING))
1139 goto out;
1140
1141 exit_code = EXIT_SUCCESS;
1142 out:
1143 if (fd_peer_pidfd >= 0)
1144 close(fd_peer_pidfd);
1145 if (fd_coredump >= 0)
1146 close(fd_coredump);
1147 if (fd_server >= 0)
1148 close(fd_server);
1149 _exit(exit_code);
1150 }
1151 self->pid_coredump_server = pid_coredump_server;
1152
1153 EXPECT_EQ(close(ipc_sockets[1]), 0);
1154 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
1155 EXPECT_EQ(close(ipc_sockets[0]), 0);
1156
1157 pid = fork();
1158 ASSERT_GE(pid, 0);
1159 if (pid == 0)
1160 crashing_child();
1161
1162 pidfd = sys_pidfd_open(pid, 0);
1163 ASSERT_GE(pidfd, 0);
1164
1165 waitpid(pid, &status, 0);
1166 ASSERT_TRUE(WIFSIGNALED(status));
1167 ASSERT_FALSE(WCOREDUMP(status));
1168
1169 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
1170 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
1171 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
1172
1173 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
1174 }
1175
TEST_F(coredump,socket_request_unknown_flag)1176 TEST_F(coredump, socket_request_unknown_flag)
1177 {
1178 int pidfd, ret, status;
1179 pid_t pid, pid_coredump_server;
1180 struct pidfd_info info = {};
1181 int ipc_sockets[2];
1182 char c;
1183
1184 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
1185
1186 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
1187 ASSERT_EQ(ret, 0);
1188
1189 pid_coredump_server = fork();
1190 ASSERT_GE(pid_coredump_server, 0);
1191 if (pid_coredump_server == 0) {
1192 struct coredump_req req = {};
1193 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
1194 int exit_code = EXIT_FAILURE;
1195
1196 close(ipc_sockets[0]);
1197
1198 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
1199 if (fd_server < 0)
1200 goto out;
1201
1202 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
1203 goto out;
1204
1205 close(ipc_sockets[1]);
1206
1207 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
1208 if (fd_coredump < 0)
1209 goto out;
1210
1211 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
1212 if (fd_peer_pidfd < 0)
1213 goto out;
1214
1215 if (!get_pidfd_info(fd_peer_pidfd, &info))
1216 goto out;
1217
1218 if (!(info.mask & PIDFD_INFO_COREDUMP))
1219 goto out;
1220
1221 if (!(info.coredump_mask & PIDFD_COREDUMPED))
1222 goto out;
1223
1224 if (!read_coredump_req(fd_coredump, &req))
1225 goto out;
1226
1227 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
1228 COREDUMP_KERNEL | COREDUMP_USERSPACE |
1229 COREDUMP_REJECT | COREDUMP_WAIT))
1230 goto out;
1231
1232 if (!send_coredump_ack(fd_coredump, &req, (1ULL << 63), 0))
1233 goto out;
1234
1235 if (!read_marker(fd_coredump, COREDUMP_MARK_UNSUPPORTED))
1236 goto out;
1237
1238 exit_code = EXIT_SUCCESS;
1239 out:
1240 if (fd_peer_pidfd >= 0)
1241 close(fd_peer_pidfd);
1242 if (fd_coredump >= 0)
1243 close(fd_coredump);
1244 if (fd_server >= 0)
1245 close(fd_server);
1246 _exit(exit_code);
1247 }
1248 self->pid_coredump_server = pid_coredump_server;
1249
1250 EXPECT_EQ(close(ipc_sockets[1]), 0);
1251 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
1252 EXPECT_EQ(close(ipc_sockets[0]), 0);
1253
1254 pid = fork();
1255 ASSERT_GE(pid, 0);
1256 if (pid == 0)
1257 crashing_child();
1258
1259 pidfd = sys_pidfd_open(pid, 0);
1260 ASSERT_GE(pidfd, 0);
1261
1262 waitpid(pid, &status, 0);
1263 ASSERT_TRUE(WIFSIGNALED(status));
1264 ASSERT_FALSE(WCOREDUMP(status));
1265
1266 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
1267 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
1268 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
1269
1270 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
1271 }
1272
TEST_F(coredump,socket_request_invalid_size_small)1273 TEST_F(coredump, socket_request_invalid_size_small)
1274 {
1275 int pidfd, ret, status;
1276 pid_t pid, pid_coredump_server;
1277 struct pidfd_info info = {};
1278 int ipc_sockets[2];
1279 char c;
1280
1281 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
1282
1283 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
1284 ASSERT_EQ(ret, 0);
1285
1286 pid_coredump_server = fork();
1287 ASSERT_GE(pid_coredump_server, 0);
1288 if (pid_coredump_server == 0) {
1289 struct coredump_req req = {};
1290 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
1291 int exit_code = EXIT_FAILURE;
1292
1293 close(ipc_sockets[0]);
1294
1295 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
1296 if (fd_server < 0)
1297 goto out;
1298
1299 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
1300 goto out;
1301
1302 close(ipc_sockets[1]);
1303
1304 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
1305 if (fd_coredump < 0)
1306 goto out;
1307
1308 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
1309 if (fd_peer_pidfd < 0)
1310 goto out;
1311
1312 if (!get_pidfd_info(fd_peer_pidfd, &info))
1313 goto out;
1314
1315 if (!(info.mask & PIDFD_INFO_COREDUMP))
1316 goto out;
1317
1318 if (!(info.coredump_mask & PIDFD_COREDUMPED))
1319 goto out;
1320
1321 if (!read_coredump_req(fd_coredump, &req))
1322 goto out;
1323
1324 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
1325 COREDUMP_KERNEL | COREDUMP_USERSPACE |
1326 COREDUMP_REJECT | COREDUMP_WAIT))
1327 goto out;
1328
1329 if (!send_coredump_ack(fd_coredump, &req,
1330 COREDUMP_REJECT | COREDUMP_WAIT,
1331 COREDUMP_ACK_SIZE_VER0 / 2))
1332 goto out;
1333
1334 if (!read_marker(fd_coredump, COREDUMP_MARK_MINSIZE))
1335 goto out;
1336
1337 exit_code = EXIT_SUCCESS;
1338 out:
1339 if (fd_peer_pidfd >= 0)
1340 close(fd_peer_pidfd);
1341 if (fd_coredump >= 0)
1342 close(fd_coredump);
1343 if (fd_server >= 0)
1344 close(fd_server);
1345 _exit(exit_code);
1346 }
1347 self->pid_coredump_server = pid_coredump_server;
1348
1349 EXPECT_EQ(close(ipc_sockets[1]), 0);
1350 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
1351 EXPECT_EQ(close(ipc_sockets[0]), 0);
1352
1353 pid = fork();
1354 ASSERT_GE(pid, 0);
1355 if (pid == 0)
1356 crashing_child();
1357
1358 pidfd = sys_pidfd_open(pid, 0);
1359 ASSERT_GE(pidfd, 0);
1360
1361 waitpid(pid, &status, 0);
1362 ASSERT_TRUE(WIFSIGNALED(status));
1363 ASSERT_FALSE(WCOREDUMP(status));
1364
1365 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
1366 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
1367 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
1368
1369 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
1370 }
1371
TEST_F(coredump,socket_request_invalid_size_large)1372 TEST_F(coredump, socket_request_invalid_size_large)
1373 {
1374 int pidfd, ret, status;
1375 pid_t pid, pid_coredump_server;
1376 struct pidfd_info info = {};
1377 int ipc_sockets[2];
1378 char c;
1379
1380 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
1381
1382 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
1383 ASSERT_EQ(ret, 0);
1384
1385 pid_coredump_server = fork();
1386 ASSERT_GE(pid_coredump_server, 0);
1387 if (pid_coredump_server == 0) {
1388 struct coredump_req req = {};
1389 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
1390 int exit_code = EXIT_FAILURE;
1391
1392 close(ipc_sockets[0]);
1393
1394 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
1395 if (fd_server < 0)
1396 goto out;
1397
1398 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
1399 goto out;
1400
1401 close(ipc_sockets[1]);
1402
1403 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
1404 if (fd_coredump < 0)
1405 goto out;
1406
1407 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
1408 if (fd_peer_pidfd < 0)
1409 goto out;
1410
1411 if (!get_pidfd_info(fd_peer_pidfd, &info))
1412 goto out;
1413
1414 if (!(info.mask & PIDFD_INFO_COREDUMP))
1415 goto out;
1416
1417 if (!(info.coredump_mask & PIDFD_COREDUMPED))
1418 goto out;
1419
1420 if (!read_coredump_req(fd_coredump, &req))
1421 goto out;
1422
1423 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
1424 COREDUMP_KERNEL | COREDUMP_USERSPACE |
1425 COREDUMP_REJECT | COREDUMP_WAIT))
1426 goto out;
1427
1428 if (!send_coredump_ack(fd_coredump, &req,
1429 COREDUMP_REJECT | COREDUMP_WAIT,
1430 COREDUMP_ACK_SIZE_VER0 + PAGE_SIZE))
1431 goto out;
1432
1433 if (!read_marker(fd_coredump, COREDUMP_MARK_MAXSIZE))
1434 goto out;
1435
1436 exit_code = EXIT_SUCCESS;
1437 out:
1438 if (fd_peer_pidfd >= 0)
1439 close(fd_peer_pidfd);
1440 if (fd_coredump >= 0)
1441 close(fd_coredump);
1442 if (fd_server >= 0)
1443 close(fd_server);
1444 _exit(exit_code);
1445 }
1446 self->pid_coredump_server = pid_coredump_server;
1447
1448 EXPECT_EQ(close(ipc_sockets[1]), 0);
1449 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
1450 EXPECT_EQ(close(ipc_sockets[0]), 0);
1451
1452 pid = fork();
1453 ASSERT_GE(pid, 0);
1454 if (pid == 0)
1455 crashing_child();
1456
1457 pidfd = sys_pidfd_open(pid, 0);
1458 ASSERT_GE(pidfd, 0);
1459
1460 waitpid(pid, &status, 0);
1461 ASSERT_TRUE(WIFSIGNALED(status));
1462 ASSERT_FALSE(WCOREDUMP(status));
1463
1464 ASSERT_TRUE(get_pidfd_info(pidfd, &info));
1465 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
1466 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
1467
1468 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
1469 }
1470
open_coredump_tmpfile(int fd_tmpfs_detached)1471 static int open_coredump_tmpfile(int fd_tmpfs_detached)
1472 {
1473 return openat(fd_tmpfs_detached, ".", O_TMPFILE | O_RDWR | O_EXCL, 0600);
1474 }
1475
1476 #define NUM_CRASHING_COREDUMPS 5
1477
1478 TEST_F_TIMEOUT(coredump, socket_multiple_crashing_coredumps, 500)
1479 {
1480 int pidfd[NUM_CRASHING_COREDUMPS], status[NUM_CRASHING_COREDUMPS];
1481 pid_t pid[NUM_CRASHING_COREDUMPS], pid_coredump_server;
1482 struct pidfd_info info = {};
1483 int ipc_sockets[2];
1484 char c;
1485
1486 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
1487
1488 ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
1489
1490 pid_coredump_server = fork();
1491 ASSERT_GE(pid_coredump_server, 0);
1492 if (pid_coredump_server == 0) {
1493 int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1;
1494 int exit_code = EXIT_FAILURE;
1495 struct coredump_req req = {};
1496
1497 close(ipc_sockets[0]);
1498 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
1499 if (fd_server < 0) {
1500 fprintf(stderr, "Failed to create and listen on unix socket\n");
1501 goto out;
1502 }
1503
1504 if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
1505 fprintf(stderr, "Failed to notify parent via ipc socket\n");
1506 goto out;
1507 }
1508 close(ipc_sockets[1]);
1509
1510 for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) {
1511 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
1512 if (fd_coredump < 0) {
1513 fprintf(stderr, "accept4 failed: %m\n");
1514 goto out;
1515 }
1516
1517 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
1518 if (fd_peer_pidfd < 0) {
1519 fprintf(stderr, "get_peer_pidfd failed for fd %d: %m\n", fd_coredump);
1520 goto out;
1521 }
1522
1523 if (!get_pidfd_info(fd_peer_pidfd, &info)) {
1524 fprintf(stderr, "get_pidfd_info failed for fd %d\n", fd_peer_pidfd);
1525 goto out;
1526 }
1527
1528 if (!(info.mask & PIDFD_INFO_COREDUMP)) {
1529 fprintf(stderr, "pidfd info missing PIDFD_INFO_COREDUMP for fd %d\n", fd_peer_pidfd);
1530 goto out;
1531 }
1532 if (!(info.coredump_mask & PIDFD_COREDUMPED)) {
1533 fprintf(stderr, "pidfd info missing PIDFD_COREDUMPED for fd %d\n", fd_peer_pidfd);
1534 goto out;
1535 }
1536
1537 if (!read_coredump_req(fd_coredump, &req)) {
1538 fprintf(stderr, "read_coredump_req failed for fd %d\n", fd_coredump);
1539 goto out;
1540 }
1541
1542 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
1543 COREDUMP_KERNEL | COREDUMP_USERSPACE |
1544 COREDUMP_REJECT | COREDUMP_WAIT)) {
1545 fprintf(stderr, "check_coredump_req failed for fd %d\n", fd_coredump);
1546 goto out;
1547 }
1548
1549 if (!send_coredump_ack(fd_coredump, &req,
1550 COREDUMP_KERNEL | COREDUMP_WAIT, 0)) {
1551 fprintf(stderr, "send_coredump_ack failed for fd %d\n", fd_coredump);
1552 goto out;
1553 }
1554
1555 if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK)) {
1556 fprintf(stderr, "read_marker failed for fd %d\n", fd_coredump);
1557 goto out;
1558 }
1559
1560 fd_core_file = open_coredump_tmpfile(self->fd_tmpfs_detached);
1561 if (fd_core_file < 0) {
1562 fprintf(stderr, "%m - open_coredump_tmpfile failed for fd %d\n", fd_coredump);
1563 goto out;
1564 }
1565
1566 for (;;) {
1567 char buffer[4096];
1568 ssize_t bytes_read, bytes_write;
1569
1570 bytes_read = read(fd_coredump, buffer, sizeof(buffer));
1571 if (bytes_read < 0) {
1572 fprintf(stderr, "read failed for fd %d: %m\n", fd_coredump);
1573 goto out;
1574 }
1575
1576 if (bytes_read == 0)
1577 break;
1578
1579 bytes_write = write(fd_core_file, buffer, bytes_read);
1580 if (bytes_read != bytes_write) {
1581 fprintf(stderr, "write failed for fd %d: %m\n", fd_core_file);
1582 goto out;
1583 }
1584 }
1585
1586 close(fd_core_file);
1587 close(fd_peer_pidfd);
1588 close(fd_coredump);
1589 fd_peer_pidfd = -1;
1590 fd_coredump = -1;
1591 }
1592
1593 exit_code = EXIT_SUCCESS;
1594 out:
1595 if (fd_core_file >= 0)
1596 close(fd_core_file);
1597 if (fd_peer_pidfd >= 0)
1598 close(fd_peer_pidfd);
1599 if (fd_coredump >= 0)
1600 close(fd_coredump);
1601 if (fd_server >= 0)
1602 close(fd_server);
1603 _exit(exit_code);
1604 }
1605 self->pid_coredump_server = pid_coredump_server;
1606
1607 EXPECT_EQ(close(ipc_sockets[1]), 0);
1608 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
1609 EXPECT_EQ(close(ipc_sockets[0]), 0);
1610
1611 for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) {
1612 pid[i] = fork();
1613 ASSERT_GE(pid[i], 0);
1614 if (pid[i] == 0)
1615 crashing_child();
1616 pidfd[i] = sys_pidfd_open(pid[i], 0);
1617 ASSERT_GE(pidfd[i], 0);
1618 }
1619
1620 for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) {
1621 waitpid(pid[i], &status[i], 0);
1622 ASSERT_TRUE(WIFSIGNALED(status[i]));
1623 ASSERT_TRUE(WCOREDUMP(status[i]));
1624 }
1625
1626 for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) {
1627 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
1628 ASSERT_EQ(ioctl(pidfd[i], PIDFD_GET_INFO, &info), 0);
1629 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
1630 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
1631 }
1632
1633 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
1634 }
1635
1636 #define MAX_EVENTS 128
1637
process_coredump_worker(int fd_coredump,int fd_peer_pidfd,int fd_core_file)1638 static void process_coredump_worker(int fd_coredump, int fd_peer_pidfd, int fd_core_file)
1639 {
1640 int epfd = -1;
1641 int exit_code = EXIT_FAILURE;
1642
1643 epfd = epoll_create1(0);
1644 if (epfd < 0)
1645 goto out;
1646
1647 struct epoll_event ev;
1648 ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
1649 ev.data.fd = fd_coredump;
1650 if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd_coredump, &ev) < 0)
1651 goto out;
1652
1653 for (;;) {
1654 struct epoll_event events[1];
1655 int n = epoll_wait(epfd, events, 1, -1);
1656 if (n < 0)
1657 break;
1658
1659 if (events[0].events & (EPOLLIN | EPOLLRDHUP)) {
1660 for (;;) {
1661 char buffer[4096];
1662 ssize_t bytes_read = read(fd_coredump, buffer, sizeof(buffer));
1663 if (bytes_read < 0) {
1664 if (errno == EAGAIN || errno == EWOULDBLOCK)
1665 break;
1666 goto out;
1667 }
1668 if (bytes_read == 0)
1669 goto done;
1670 ssize_t bytes_write = write(fd_core_file, buffer, bytes_read);
1671 if (bytes_write != bytes_read)
1672 goto out;
1673 }
1674 }
1675 }
1676
1677 done:
1678 exit_code = EXIT_SUCCESS;
1679 out:
1680 if (epfd >= 0)
1681 close(epfd);
1682 if (fd_core_file >= 0)
1683 close(fd_core_file);
1684 if (fd_peer_pidfd >= 0)
1685 close(fd_peer_pidfd);
1686 if (fd_coredump >= 0)
1687 close(fd_coredump);
1688 _exit(exit_code);
1689 }
1690
1691 TEST_F_TIMEOUT(coredump, socket_multiple_crashing_coredumps_epoll_workers, 500)
1692 {
1693 int pidfd[NUM_CRASHING_COREDUMPS], status[NUM_CRASHING_COREDUMPS];
1694 pid_t pid[NUM_CRASHING_COREDUMPS], pid_coredump_server, worker_pids[NUM_CRASHING_COREDUMPS];
1695 struct pidfd_info info = {};
1696 int ipc_sockets[2];
1697 char c;
1698
1699 ASSERT_TRUE(set_core_pattern("@@/tmp/coredump.socket"));
1700 ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
1701
1702 pid_coredump_server = fork();
1703 ASSERT_GE(pid_coredump_server, 0);
1704 if (pid_coredump_server == 0) {
1705 int fd_server = -1, exit_code = EXIT_FAILURE, n_conns = 0;
1706 fd_server = -1;
1707 exit_code = EXIT_FAILURE;
1708 n_conns = 0;
1709 close(ipc_sockets[0]);
1710 fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
1711 if (fd_server < 0)
1712 goto out;
1713
1714 if (write_nointr(ipc_sockets[1], "1", 1) < 0)
1715 goto out;
1716 close(ipc_sockets[1]);
1717
1718 while (n_conns < NUM_CRASHING_COREDUMPS) {
1719 int fd_coredump = -1, fd_peer_pidfd = -1, fd_core_file = -1;
1720 struct coredump_req req = {};
1721 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
1722 if (fd_coredump < 0) {
1723 if (errno == EAGAIN || errno == EWOULDBLOCK)
1724 continue;
1725 goto out;
1726 }
1727 fd_peer_pidfd = get_peer_pidfd(fd_coredump);
1728 if (fd_peer_pidfd < 0)
1729 goto out;
1730 if (!get_pidfd_info(fd_peer_pidfd, &info))
1731 goto out;
1732 if (!(info.mask & PIDFD_INFO_COREDUMP) || !(info.coredump_mask & PIDFD_COREDUMPED))
1733 goto out;
1734 if (!read_coredump_req(fd_coredump, &req))
1735 goto out;
1736 if (!check_coredump_req(&req, COREDUMP_ACK_SIZE_VER0,
1737 COREDUMP_KERNEL | COREDUMP_USERSPACE |
1738 COREDUMP_REJECT | COREDUMP_WAIT))
1739 goto out;
1740 if (!send_coredump_ack(fd_coredump, &req, COREDUMP_KERNEL | COREDUMP_WAIT, 0))
1741 goto out;
1742 if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK))
1743 goto out;
1744 fd_core_file = open_coredump_tmpfile(self->fd_tmpfs_detached);
1745 if (fd_core_file < 0)
1746 goto out;
1747 pid_t worker = fork();
1748 if (worker == 0) {
1749 close(fd_server);
1750 process_coredump_worker(fd_coredump, fd_peer_pidfd, fd_core_file);
1751 }
1752 worker_pids[n_conns] = worker;
1753 if (fd_coredump >= 0)
1754 close(fd_coredump);
1755 if (fd_peer_pidfd >= 0)
1756 close(fd_peer_pidfd);
1757 if (fd_core_file >= 0)
1758 close(fd_core_file);
1759 n_conns++;
1760 }
1761 exit_code = EXIT_SUCCESS;
1762 out:
1763 if (fd_server >= 0)
1764 close(fd_server);
1765
1766 // Reap all worker processes
1767 for (int i = 0; i < n_conns; i++) {
1768 int wstatus;
1769 if (waitpid(worker_pids[i], &wstatus, 0) < 0) {
1770 fprintf(stderr, "Failed to wait for worker %d: %m\n", worker_pids[i]);
1771 } else if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != EXIT_SUCCESS) {
1772 fprintf(stderr, "Worker %d exited with error code %d\n", worker_pids[i], WEXITSTATUS(wstatus));
1773 exit_code = EXIT_FAILURE;
1774 }
1775 }
1776
1777 _exit(exit_code);
1778 }
1779 self->pid_coredump_server = pid_coredump_server;
1780
1781 EXPECT_EQ(close(ipc_sockets[1]), 0);
1782 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
1783 EXPECT_EQ(close(ipc_sockets[0]), 0);
1784
1785 for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) {
1786 pid[i] = fork();
1787 ASSERT_GE(pid[i], 0);
1788 if (pid[i] == 0)
1789 crashing_child();
1790 pidfd[i] = sys_pidfd_open(pid[i], 0);
1791 ASSERT_GE(pidfd[i], 0);
1792 }
1793
1794 for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) {
1795 ASSERT_GE(waitpid(pid[i], &status[i], 0), 0);
1796 ASSERT_TRUE(WIFSIGNALED(status[i]));
1797 ASSERT_TRUE(WCOREDUMP(status[i]));
1798 }
1799
1800 for (int i = 0; i < NUM_CRASHING_COREDUMPS; i++) {
1801 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
1802 ASSERT_EQ(ioctl(pidfd[i], PIDFD_GET_INFO, &info), 0);
1803 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
1804 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
1805 }
1806
1807 wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
1808 }
1809
TEST_F(coredump,socket_invalid_paths)1810 TEST_F(coredump, socket_invalid_paths)
1811 {
1812 ASSERT_FALSE(set_core_pattern("@ /tmp/coredump.socket"));
1813 ASSERT_FALSE(set_core_pattern("@/tmp/../coredump.socket"));
1814 ASSERT_FALSE(set_core_pattern("@../coredump.socket"));
1815 ASSERT_FALSE(set_core_pattern("@/tmp/coredump.socket/.."));
1816 ASSERT_FALSE(set_core_pattern("@.."));
1817
1818 ASSERT_FALSE(set_core_pattern("@@ /tmp/coredump.socket"));
1819 ASSERT_FALSE(set_core_pattern("@@/tmp/../coredump.socket"));
1820 ASSERT_FALSE(set_core_pattern("@@../coredump.socket"));
1821 ASSERT_FALSE(set_core_pattern("@@/tmp/coredump.socket/.."));
1822 ASSERT_FALSE(set_core_pattern("@@.."));
1823
1824 ASSERT_FALSE(set_core_pattern("@@@/tmp/coredump.socket"));
1825 }
1826
1827 TEST_HARNESS_MAIN
1828