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 close(fd_coredump);
465 close(fd_server);
466 close(fd_peer_pidfd);
467 close(fd_core_file);
468 _exit(EXIT_SUCCESS);
469 }
470 self->pid_coredump_server = pid_coredump_server;
471
472 EXPECT_EQ(close(ipc_sockets[1]), 0);
473 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
474 EXPECT_EQ(close(ipc_sockets[0]), 0);
475
476 pid = fork();
477 ASSERT_GE(pid, 0);
478 if (pid == 0) {
479 int fd_socket;
480 ssize_t ret;
481
482 fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
483 if (fd_socket < 0)
484 _exit(EXIT_FAILURE);
485
486
487 ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
488 if (ret < 0)
489 _exit(EXIT_FAILURE);
490
491 (void *)write(fd_socket, &(char){ 0 }, 1);
492 close(fd_socket);
493 _exit(EXIT_SUCCESS);
494 }
495
496 pidfd = sys_pidfd_open(pid, 0);
497 ASSERT_GE(pidfd, 0);
498
499 waitpid(pid, &status, 0);
500 ASSERT_TRUE(WIFEXITED(status));
501 ASSERT_EQ(WEXITSTATUS(status), 0);
502
503 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP;
504 ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0);
505 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0);
506 ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0);
507
508 waitpid(pid_coredump_server, &status, 0);
509 self->pid_coredump_server = -ESRCH;
510 ASSERT_TRUE(WIFEXITED(status));
511 ASSERT_EQ(WEXITSTATUS(status), 0);
512
513 ASSERT_NE(stat("/tmp/coredump.file", &st), 0);
514 ASSERT_EQ(errno, ENOENT);
515}
516
517TEST_F(coredump, socket_enoent)
518{
519 int pidfd, ret, status;
520 FILE *file;
521 pid_t pid;
522 char core_file[PATH_MAX];
523
524 file = fopen("/proc/sys/kernel/core_pattern", "w");
525 ASSERT_NE(file, NULL);
526
527 ret = fprintf(file, "@/tmp/coredump.socket");
528 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
529 ASSERT_EQ(fclose(file), 0);
530
531 pid = fork();
532 ASSERT_GE(pid, 0);
533 if (pid == 0)
534 crashing_child();
535
536 pidfd = sys_pidfd_open(pid, 0);
537 ASSERT_GE(pidfd, 0);
538
539 waitpid(pid, &status, 0);
540 ASSERT_TRUE(WIFSIGNALED(status));
541 ASSERT_FALSE(WCOREDUMP(status));
542}
543
544TEST_F(coredump, socket_no_listener)
545{
546 int pidfd, ret, status;
547 FILE *file;
548 pid_t pid, pid_coredump_server;
549 int ipc_sockets[2];
550 char c;
551 const struct sockaddr_un coredump_sk = {
552 .sun_family = AF_UNIX,
553 .sun_path = "/tmp/coredump.socket",
554 };
555 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) +
556 sizeof("/tmp/coredump.socket");
557
558 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
559 ASSERT_EQ(ret, 0);
560
561 file = fopen("/proc/sys/kernel/core_pattern", "w");
562 ASSERT_NE(file, NULL);
563
564 ret = fprintf(file, "@/tmp/coredump.socket");
565 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket"));
566 ASSERT_EQ(fclose(file), 0);
567
568 pid_coredump_server = fork();
569 ASSERT_GE(pid_coredump_server, 0);
570 if (pid_coredump_server == 0) {
571 int fd_server;
572 socklen_t fd_peer_pidfd_len;
573
574 close(ipc_sockets[0]);
575
576 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
577 if (fd_server < 0)
578 _exit(EXIT_FAILURE);
579
580 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len);
581 if (ret < 0) {
582 fprintf(stderr, "Failed to bind coredump socket\n");
583 close(fd_server);
584 close(ipc_sockets[1]);
585 _exit(EXIT_FAILURE);
586 }
587
588 if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
589 close(fd_server);
590 close(ipc_sockets[1]);
591 _exit(EXIT_FAILURE);
592 }
593
594 close(fd_server);
595 close(ipc_sockets[1]);
596 _exit(EXIT_SUCCESS);
597 }
598 self->pid_coredump_server = pid_coredump_server;
599
600 EXPECT_EQ(close(ipc_sockets[1]), 0);
601 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
602 EXPECT_EQ(close(ipc_sockets[0]), 0);
603
604 pid = fork();
605 ASSERT_GE(pid, 0);
606 if (pid == 0)
607 crashing_child();
608
609 pidfd = sys_pidfd_open(pid, 0);
610 ASSERT_GE(pidfd, 0);
611
612 waitpid(pid, &status, 0);
613 ASSERT_TRUE(WIFSIGNALED(status));
614 ASSERT_FALSE(WCOREDUMP(status));
615
616 waitpid(pid_coredump_server, &status, 0);
617 self->pid_coredump_server = -ESRCH;
618 ASSERT_TRUE(WIFEXITED(status));
619 ASSERT_EQ(WEXITSTATUS(status), 0);
620}
621
622TEST_HARNESS_MAIN