Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3#include <fcntl.h>
4#include <inttypes.h>
5#include <libgen.h>
6#include <linux/limits.h>
7#include <pthread.h>
8#include <string.h>
9#include <sys/mount.h>
10#include <sys/resource.h>
11#include <sys/stat.h>
12#include <sys/socket.h>
13#include <sys/un.h>
14#include <unistd.h>
15
16#include "../kselftest_harness.h"
17#include "../pidfd/pidfd.h"
18
19#define STACKDUMP_FILE "stack_values"
20#define STACKDUMP_SCRIPT "stackdump"
21#define NUM_THREAD_SPAWN 128
22
23static void *do_nothing(void *)
24{
25 while (1)
26 pause();
27}
28
29static void crashing_child(void)
30{
31 pthread_t thread;
32 int i;
33
34 for (i = 0; i < NUM_THREAD_SPAWN; ++i)
35 pthread_create(&thread, NULL, do_nothing, NULL);
36
37 /* crash on purpose */
38 i = *(int *)NULL;
39}
40
41FIXTURE(coredump)
42{
43 char original_core_pattern[256];
44 pid_t pid_coredump_server;
45};
46
47FIXTURE_SETUP(coredump)
48{
49 char buf[PATH_MAX];
50 FILE *file;
51 char *dir;
52 int ret;
53
54 self->pid_coredump_server = -ESRCH;
55 file = fopen("/proc/sys/kernel/core_pattern", "r");
56 ASSERT_NE(NULL, file);
57
58 ret = fread(self->original_core_pattern, 1, sizeof(self->original_core_pattern), file);
59 ASSERT_TRUE(ret || feof(file));
60 ASSERT_LT(ret, sizeof(self->original_core_pattern));
61
62 self->original_core_pattern[ret] = '\0';
63
64 ret = fclose(file);
65 ASSERT_EQ(0, ret);
66}
67
68FIXTURE_TEARDOWN(coredump)
69{
70 const char *reason;
71 FILE *file;
72 int ret, status;
73
74 unlink(STACKDUMP_FILE);
75
76 if (self->pid_coredump_server > 0) {
77 kill(self->pid_coredump_server, SIGTERM);
78 waitpid(self->pid_coredump_server, &status, 0);
79 }
80 unlink("/tmp/coredump.file");
81 unlink("/tmp/coredump.socket");
82
83 file = fopen("/proc/sys/kernel/core_pattern", "w");
84 if (!file) {
85 reason = "Unable to open core_pattern";
86 goto fail;
87 }
88
89 ret = fprintf(file, "%s", self->original_core_pattern);
90 if (ret < 0) {
91 reason = "Unable to write to core_pattern";
92 goto fail;
93 }
94
95 ret = fclose(file);
96 if (ret) {
97 reason = "Unable to close core_pattern";
98 goto fail;
99 }
100
101 return;
102fail:
103 /* This should never happen */
104 fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason);
105}
106
107TEST_F_TIMEOUT(coredump, stackdump, 120)
108{
109 struct sigaction action = {};
110 unsigned long long stack;
111 char *test_dir, *line;
112 size_t line_length;
113 char buf[PATH_MAX];
114 int ret, i, status;
115 FILE *file;
116 pid_t pid;
117
118 /*
119 * Step 1: Setup core_pattern so that the stackdump script is executed when the child
120 * process crashes
121 */
122 ret = readlink("/proc/self/exe", buf, sizeof(buf));
123 ASSERT_NE(-1, ret);
124 ASSERT_LT(ret, sizeof(buf));
125 buf[ret] = '\0';
126
127 test_dir = dirname(buf);
128
129 file = fopen("/proc/sys/kernel/core_pattern", "w");
130 ASSERT_NE(NULL, file);
131
132 ret = fprintf(file, "|%1$s/%2$s %%P %1$s/%3$s", test_dir, STACKDUMP_SCRIPT, STACKDUMP_FILE);
133 ASSERT_LT(0, ret);
134
135 ret = fclose(file);
136 ASSERT_EQ(0, ret);
137
138 /* Step 2: Create a process who spawns some threads then crashes */
139 pid = fork();
140 ASSERT_TRUE(pid >= 0);
141 if (pid == 0)
142 crashing_child();
143
144 /*
145 * Step 3: Wait for the stackdump script to write the stack pointers to the stackdump file
146 */
147 waitpid(pid, &status, 0);
148 ASSERT_TRUE(WIFSIGNALED(status));
149 ASSERT_TRUE(WCOREDUMP(status));
150
151 for (i = 0; i < 10; ++i) {
152 file = fopen(STACKDUMP_FILE, "r");
153 if (file)
154 break;
155 sleep(1);
156 }
157 ASSERT_NE(file, NULL);
158
159 /* Step 4: Make sure all stack pointer values are non-zero */
160 line = NULL;
161 for (i = 0; -1 != getline(&line, &line_length, file); ++i) {
162 stack = strtoull(line, NULL, 10);
163 ASSERT_NE(stack, 0);
164 }
165 free(line);
166
167 ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN);
168
169 fclose(file);
170}
171
172TEST_F(coredump, socket)
173{
174 int fd, pidfd, ret, status;
175 FILE *file;
176 pid_t pid, pid_coredump_server;
177 struct stat st;
178 char core_file[PATH_MAX];
179 struct pidfd_info info = {};
180 int ipc_sockets[2];
181 char c;
182 const struct sockaddr_un coredump_sk = {
183 .sun_family = AF_UNIX,
184 .sun_path = "/tmp/coredump.socket",
185 };
186 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
187 sizeof("/tmp/coredump.socket");
188
189 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
190 ASSERT_EQ(ret, 0);
191
192 file = fopen("/proc/sys/kernel/core_pattern", "w");
193 ASSERT_NE(file, NULL);
194
195 ret = fprintf(file, "@/tmp/coredump.socket");
196 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
197 ASSERT_EQ(fclose(file), 0);
198
199 pid_coredump_server = fork();
200 ASSERT_GE(pid_coredump_server, 0);
201 if (pid_coredump_server == 0) {
202 int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file;
203 socklen_t fd_peer_pidfd_len;
204
205 close(ipc_sockets[0]);
206
207 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
208 if (fd_server < 0)
209 _exit(EXIT_FAILURE);
210
211 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
212 if (ret < 0) {
213 fprintf(stderr, "Failed to bind coredump socket\n");
214 close(fd_server);
215 close(ipc_sockets[1]);
216 _exit(EXIT_FAILURE);
217 }
218
219 ret = listen(fd_server, 1);
220 if (ret < 0) {
221 fprintf(stderr, "Failed to listen on coredump socket\n");
222 close(fd_server);
223 close(ipc_sockets[1]);
224 _exit(EXIT_FAILURE);
225 }
226
227 if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
228 close(fd_server);
229 close(ipc_sockets[1]);
230 _exit(EXIT_FAILURE);
231 }
232
233 close(ipc_sockets[1]);
234
235 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
236 if (fd_coredump < 0) {
237 fprintf(stderr, "Failed to accept coredump socket connection\n");
238 close(fd_server);
239 _exit(EXIT_FAILURE);
240 }
241
242 fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
243 ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD,
244 &fd_peer_pidfd, &fd_peer_pidfd_len);
245 if (ret < 0) {
246 fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n");
247 close(fd_coredump);
248 close(fd_server);
249 _exit(EXIT_FAILURE);
250 }
251
252 memset(&info, 0, sizeof(info));
253 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
254 ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info);
255 if (ret < 0) {
256 fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n");
257 close(fd_coredump);
258 close(fd_server);
259 close(fd_peer_pidfd);
260 _exit(EXIT_FAILURE);
261 }
262
263 if (!(info.mask & PIDFD_INFO_COREDUMP)) {
264 fprintf(stderr, "Missing coredump information from coredumping task\n");
265 close(fd_coredump);
266 close(fd_server);
267 close(fd_peer_pidfd);
268 _exit(EXIT_FAILURE);
269 }
270
271 if (!(info.coredump_mask & PIDFD_COREDUMPED)) {
272 fprintf(stderr, "Received connection from non-coredumping task\n");
273 close(fd_coredump);
274 close(fd_server);
275 close(fd_peer_pidfd);
276 _exit(EXIT_FAILURE);
277 }
278
279 fd_core_file = creat("/tmp/coredump.file", 0644);
280 if (fd_core_file < 0) {
281 fprintf(stderr, "Failed to create coredump file\n");
282 close(fd_coredump);
283 close(fd_server);
284 close(fd_peer_pidfd);
285 _exit(EXIT_FAILURE);
286 }
287
288 for (;;) {
289 char buffer[4096];
290 ssize_t bytes_read, bytes_write;
291
292 bytes_read = read(fd_coredump, buffer, sizeof(buffer));
293 if (bytes_read < 0) {
294 close(fd_coredump);
295 close(fd_server);
296 close(fd_peer_pidfd);
297 close(fd_core_file);
298 _exit(EXIT_FAILURE);
299 }
300
301 if (bytes_read == 0)
302 break;
303
304 bytes_write = write(fd_core_file, buffer, bytes_read);
305 if (bytes_read != bytes_write) {
306 close(fd_coredump);
307 close(fd_server);
308 close(fd_peer_pidfd);
309 close(fd_core_file);
310 _exit(EXIT_FAILURE);
311 }
312 }
313
314 close(fd_coredump);
315 close(fd_server);
316 close(fd_peer_pidfd);
317 close(fd_core_file);
318 _exit(EXIT_SUCCESS);
319 }
320 self->pid_coredump_server = pid_coredump_server;
321
322 EXPECT_EQ(close(ipc_sockets[1]), 0);
323 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
324 EXPECT_EQ(close(ipc_sockets[0]), 0);
325
326 pid = fork();
327 ASSERT_GE(pid, 0);
328 if (pid == 0)
329 crashing_child();
330
331 pidfd = sys_pidfd_open(pid, 0);
332 ASSERT_GE(pidfd, 0);
333
334 waitpid(pid, &status, 0);
335 ASSERT_TRUE(WIFSIGNALED(status));
336 ASSERT_TRUE(WCOREDUMP(status));
337
338 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
339 ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0);
340 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
341 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0);
342
343 waitpid(pid_coredump_server, &status, 0);
344 self->pid_coredump_server = -ESRCH;
345 ASSERT_TRUE(WIFEXITED(status));
346 ASSERT_EQ(WEXITSTATUS(status), 0);
347
348 ASSERT_EQ(stat("/tmp/coredump.file", &st), 0);
349 ASSERT_GT(st.st_size, 0);
350 /*
351 * We should somehow validate the produced core file.
352 * For now just allow for visual inspection
353 */
354 system("file /tmp/coredump.file");
355}
356
357TEST_F(coredump, socket_detect_userspace_client)
358{
359 int fd, pidfd, ret, status;
360 FILE *file;
361 pid_t pid, pid_coredump_server;
362 struct stat st;
363 char core_file[PATH_MAX];
364 struct pidfd_info info = {};
365 int ipc_sockets[2];
366 char c;
367 const struct sockaddr_un coredump_sk = {
368 .sun_family = AF_UNIX,
369 .sun_path = "/tmp/coredump.socket",
370 };
371 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
372 sizeof("/tmp/coredump.socket");
373
374 file = fopen("/proc/sys/kernel/core_pattern", "w");
375 ASSERT_NE(file, NULL);
376
377 ret = fprintf(file, "@/tmp/coredump.socket");
378 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
379 ASSERT_EQ(fclose(file), 0);
380
381 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
382 ASSERT_EQ(ret, 0);
383
384 pid_coredump_server = fork();
385 ASSERT_GE(pid_coredump_server, 0);
386 if (pid_coredump_server == 0) {
387 int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file;
388 socklen_t fd_peer_pidfd_len;
389
390 close(ipc_sockets[0]);
391
392 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
393 if (fd_server < 0)
394 _exit(EXIT_FAILURE);
395
396 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
397 if (ret < 0) {
398 fprintf(stderr, "Failed to bind coredump socket\n");
399 close(fd_server);
400 close(ipc_sockets[1]);
401 _exit(EXIT_FAILURE);
402 }
403
404 ret = listen(fd_server, 1);
405 if (ret < 0) {
406 fprintf(stderr, "Failed to listen on coredump socket\n");
407 close(fd_server);
408 close(ipc_sockets[1]);
409 _exit(EXIT_FAILURE);
410 }
411
412 if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
413 close(fd_server);
414 close(ipc_sockets[1]);
415 _exit(EXIT_FAILURE);
416 }
417
418 close(ipc_sockets[1]);
419
420 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
421 if (fd_coredump < 0) {
422 fprintf(stderr, "Failed to accept coredump socket connection\n");
423 close(fd_server);
424 _exit(EXIT_FAILURE);
425 }
426
427 fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
428 ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD,
429 &fd_peer_pidfd, &fd_peer_pidfd_len);
430 if (ret < 0) {
431 fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n");
432 close(fd_coredump);
433 close(fd_server);
434 _exit(EXIT_FAILURE);
435 }
436
437 memset(&info, 0, sizeof(info));
438 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
439 ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info);
440 if (ret < 0) {
441 fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n");
442 close(fd_coredump);
443 close(fd_server);
444 close(fd_peer_pidfd);
445 _exit(EXIT_FAILURE);
446 }
447
448 if (!(info.mask & PIDFD_INFO_COREDUMP)) {
449 fprintf(stderr, "Missing coredump information from coredumping task\n");
450 close(fd_coredump);
451 close(fd_server);
452 close(fd_peer_pidfd);
453 _exit(EXIT_FAILURE);
454 }
455
456 if (info.coredump_mask & PIDFD_COREDUMPED) {
457 fprintf(stderr, "Received unexpected connection from coredumping task\n");
458 close(fd_coredump);
459 close(fd_server);
460 close(fd_peer_pidfd);
461 _exit(EXIT_FAILURE);
462 }
463
464 ret = read(fd_coredump, &c, 1);
465
466 close(fd_coredump);
467 close(fd_server);
468 close(fd_peer_pidfd);
469 close(fd_core_file);
470
471 if (ret < 1)
472 _exit(EXIT_FAILURE);
473 _exit(EXIT_SUCCESS);
474 }
475 self->pid_coredump_server = pid_coredump_server;
476
477 EXPECT_EQ(close(ipc_sockets[1]), 0);
478 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
479 EXPECT_EQ(close(ipc_sockets[0]), 0);
480
481 pid = fork();
482 ASSERT_GE(pid, 0);
483 if (pid == 0) {
484 int fd_socket;
485 ssize_t ret;
486
487 fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
488 if (fd_socket < 0)
489 _exit(EXIT_FAILURE);
490
491
492 ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
493 if (ret < 0)
494 _exit(EXIT_FAILURE);
495
496 (void *)write(fd_socket, &(char){ 0 }, 1);
497 close(fd_socket);
498 _exit(EXIT_SUCCESS);
499 }
500
501 pidfd = sys_pidfd_open(pid, 0);
502 ASSERT_GE(pidfd, 0);
503
504 waitpid(pid, &status, 0);
505 ASSERT_TRUE(WIFEXITED(status));
506 ASSERT_EQ(WEXITSTATUS(status), 0);
507
508 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
509 ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0);
510 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
511 ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0);
512
513 waitpid(pid_coredump_server, &status, 0);
514 self->pid_coredump_server = -ESRCH;
515 ASSERT_TRUE(WIFEXITED(status));
516 ASSERT_EQ(WEXITSTATUS(status), 0);
517
518 ASSERT_NE(stat("/tmp/coredump.file", &st), 0);
519 ASSERT_EQ(errno, ENOENT);
520}
521
522TEST_F(coredump, socket_enoent)
523{
524 int pidfd, ret, status;
525 FILE *file;
526 pid_t pid;
527 char core_file[PATH_MAX];
528
529 file = fopen("/proc/sys/kernel/core_pattern", "w");
530 ASSERT_NE(file, NULL);
531
532 ret = fprintf(file, "@/tmp/coredump.socket");
533 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
534 ASSERT_EQ(fclose(file), 0);
535
536 pid = fork();
537 ASSERT_GE(pid, 0);
538 if (pid == 0)
539 crashing_child();
540
541 pidfd = sys_pidfd_open(pid, 0);
542 ASSERT_GE(pidfd, 0);
543
544 waitpid(pid, &status, 0);
545 ASSERT_TRUE(WIFSIGNALED(status));
546 ASSERT_FALSE(WCOREDUMP(status));
547}
548
549TEST_F(coredump, socket_no_listener)
550{
551 int pidfd, ret, status;
552 FILE *file;
553 pid_t pid, pid_coredump_server;
554 int ipc_sockets[2];
555 char c;
556 const struct sockaddr_un coredump_sk = {
557 .sun_family = AF_UNIX,
558 .sun_path = "/tmp/coredump.socket",
559 };
560 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
561 sizeof("/tmp/coredump.socket");
562
563 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
564 ASSERT_EQ(ret, 0);
565
566 file = fopen("/proc/sys/kernel/core_pattern", "w");
567 ASSERT_NE(file, NULL);
568
569 ret = fprintf(file, "@/tmp/coredump.socket");
570 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
571 ASSERT_EQ(fclose(file), 0);
572
573 pid_coredump_server = fork();
574 ASSERT_GE(pid_coredump_server, 0);
575 if (pid_coredump_server == 0) {
576 int fd_server;
577 socklen_t fd_peer_pidfd_len;
578
579 close(ipc_sockets[0]);
580
581 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
582 if (fd_server < 0)
583 _exit(EXIT_FAILURE);
584
585 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
586 if (ret < 0) {
587 fprintf(stderr, "Failed to bind coredump socket\n");
588 close(fd_server);
589 close(ipc_sockets[1]);
590 _exit(EXIT_FAILURE);
591 }
592
593 if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
594 close(fd_server);
595 close(ipc_sockets[1]);
596 _exit(EXIT_FAILURE);
597 }
598
599 close(fd_server);
600 close(ipc_sockets[1]);
601 _exit(EXIT_SUCCESS);
602 }
603 self->pid_coredump_server = pid_coredump_server;
604
605 EXPECT_EQ(close(ipc_sockets[1]), 0);
606 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
607 EXPECT_EQ(close(ipc_sockets[0]), 0);
608
609 pid = fork();
610 ASSERT_GE(pid, 0);
611 if (pid == 0)
612 crashing_child();
613
614 pidfd = sys_pidfd_open(pid, 0);
615 ASSERT_GE(pidfd, 0);
616
617 waitpid(pid, &status, 0);
618 ASSERT_TRUE(WIFSIGNALED(status));
619 ASSERT_FALSE(WCOREDUMP(status));
620
621 waitpid(pid_coredump_server, &status, 0);
622 self->pid_coredump_server = -ESRCH;
623 ASSERT_TRUE(WIFEXITED(status));
624 ASSERT_EQ(WEXITSTATUS(status), 0);
625}
626
627TEST_HARNESS_MAIN