xref: /linux/tools/testing/selftests/coredump/stackdump_test.c (revision 7fc2cd2e4b398c57c9cf961cfea05eadbf34c05c)
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 #include "coredump_test.h"
27 
28 #define STACKDUMP_FILE "stack_values"
29 #define STACKDUMP_SCRIPT "stackdump"
30 
31 #ifndef PAGE_SIZE
32 #define PAGE_SIZE 4096
33 #endif
34 
35 FIXTURE_SETUP(coredump)
36 {
37 	FILE *file;
38 	int ret;
39 
40 	self->pid_coredump_server = -ESRCH;
41 	self->fd_tmpfs_detached = -1;
42 	file = fopen("/proc/sys/kernel/core_pattern", "r");
43 	ASSERT_NE(NULL, file);
44 
45 	ret = fread(self->original_core_pattern, 1, sizeof(self->original_core_pattern), file);
46 	ASSERT_TRUE(ret || feof(file));
47 	ASSERT_LT(ret, sizeof(self->original_core_pattern));
48 
49 	self->original_core_pattern[ret] = '\0';
50 	self->fd_tmpfs_detached = create_detached_tmpfs();
51 	ASSERT_GE(self->fd_tmpfs_detached, 0);
52 
53 	ret = fclose(file);
54 	ASSERT_EQ(0, ret);
55 }
56 
57 FIXTURE_TEARDOWN(coredump)
58 {
59 	const char *reason;
60 	FILE *file;
61 	int ret, status;
62 
63 	unlink(STACKDUMP_FILE);
64 
65 	if (self->pid_coredump_server > 0) {
66 		kill(self->pid_coredump_server, SIGTERM);
67 		waitpid(self->pid_coredump_server, &status, 0);
68 	}
69 	unlink("/tmp/coredump.file");
70 	unlink("/tmp/coredump.socket");
71 
72 	file = fopen("/proc/sys/kernel/core_pattern", "w");
73 	if (!file) {
74 		reason = "Unable to open core_pattern";
75 		goto fail;
76 	}
77 
78 	ret = fprintf(file, "%s", self->original_core_pattern);
79 	if (ret < 0) {
80 		reason = "Unable to write to core_pattern";
81 		goto fail;
82 	}
83 
84 	ret = fclose(file);
85 	if (ret) {
86 		reason = "Unable to close core_pattern";
87 		goto fail;
88 	}
89 
90 	if (self->fd_tmpfs_detached >= 0) {
91 		ret = close(self->fd_tmpfs_detached);
92 		if (ret < 0) {
93 			reason = "Unable to close detached tmpfs";
94 			goto fail;
95 		}
96 		self->fd_tmpfs_detached = -1;
97 	}
98 
99 	return;
100 fail:
101 	/* This should never happen */
102 	fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason);
103 }
104 
105 TEST_F_TIMEOUT(coredump, stackdump, 120)
106 {
107 	unsigned long long stack;
108 	char *test_dir, *line;
109 	size_t line_length;
110 	char buf[PAGE_SIZE];
111 	int ret, i, status;
112 	FILE *file;
113 	pid_t pid;
114 
115 	/*
116 	 * Step 1: Setup core_pattern so that the stackdump script is executed when the child
117 	 * process crashes
118 	 */
119 	ret = readlink("/proc/self/exe", buf, sizeof(buf));
120 	ASSERT_NE(-1, ret);
121 	ASSERT_LT(ret, sizeof(buf));
122 	buf[ret] = '\0';
123 
124 	test_dir = dirname(buf);
125 
126 	file = fopen("/proc/sys/kernel/core_pattern", "w");
127 	ASSERT_NE(NULL, file);
128 
129 	ret = fprintf(file, "|%1$s/%2$s %%P %1$s/%3$s", test_dir, STACKDUMP_SCRIPT, STACKDUMP_FILE);
130 	ASSERT_LT(0, ret);
131 
132 	ret = fclose(file);
133 	ASSERT_EQ(0, ret);
134 
135 	/* Step 2: Create a process who spawns some threads then crashes */
136 	pid = fork();
137 	ASSERT_TRUE(pid >= 0);
138 	if (pid == 0)
139 		crashing_child();
140 
141 	/*
142 	 * Step 3: Wait for the stackdump script to write the stack pointers to the stackdump file
143 	 */
144 	waitpid(pid, &status, 0);
145 	ASSERT_TRUE(WIFSIGNALED(status));
146 	ASSERT_TRUE(WCOREDUMP(status));
147 
148 	for (i = 0; i < 10; ++i) {
149 		file = fopen(STACKDUMP_FILE, "r");
150 		if (file)
151 			break;
152 		sleep(1);
153 	}
154 	ASSERT_NE(file, NULL);
155 
156 	/* Step 4: Make sure all stack pointer values are non-zero */
157 	line = NULL;
158 	for (i = 0; -1 != getline(&line, &line_length, file); ++i) {
159 		stack = strtoull(line, NULL, 10);
160 		ASSERT_NE(stack, 0);
161 	}
162 	free(line);
163 
164 	ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN);
165 
166 	fclose(file);
167 }
168 
169 TEST_HARNESS_MAIN
170