xref: /linux/tools/testing/selftests/coredump/stackdump_test.c (revision af2d6148d2a159e1a0862bce5a2c88c1618a2b27)
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 
23 static void *do_nothing(void *)
24 {
25 	while (1)
26 		pause();
27 }
28 
29 static 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 
41 FIXTURE(coredump)
42 {
43 	char original_core_pattern[256];
44 	pid_t pid_coredump_server;
45 };
46 
47 FIXTURE_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 
68 FIXTURE_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;
102 fail:
103 	/* This should never happen */
104 	fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason);
105 }
106 
107 TEST_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 
172 TEST_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 
357 TEST_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 
522 TEST_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 
549 TEST_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 
627 TEST_HARNESS_MAIN
628