xref: /linux/tools/testing/selftests/coredump/coredump_test_helpers.c (revision 212c4053a1502e5117d8cbbbd1c15579ce1839bb)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <assert.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <limits.h>
7 #include <linux/coredump.h>
8 #include <linux/fs.h>
9 #include <pthread.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/epoll.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <sys/un.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 
22 #include "../filesystems/wrappers.h"
23 #include "../pidfd/pidfd.h"
24 
25 /* Forward declarations to avoid including harness header */
26 struct __test_metadata;
27 
28 /* Match the fixture definition from coredump_test.h */
29 struct _fixture_coredump_data {
30 	char original_core_pattern[256];
31 	pid_t pid_coredump_server;
32 	int fd_tmpfs_detached;
33 };
34 
35 #ifndef PAGE_SIZE
36 #define PAGE_SIZE 4096
37 #endif
38 
39 #define NUM_THREAD_SPAWN 128
40 
do_nothing(void * arg)41 void *do_nothing(void *arg)
42 {
43 	(void)arg;
44 	while (1)
45 		pause();
46 
47 	return NULL;
48 }
49 
crashing_child(void)50 void crashing_child(void)
51 {
52 	pthread_t thread;
53 	int i;
54 
55 	for (i = 0; i < NUM_THREAD_SPAWN; ++i)
56 		pthread_create(&thread, NULL, do_nothing, NULL);
57 
58 	/* crash on purpose */
59 	i = *(int *)NULL;
60 }
61 
create_detached_tmpfs(void)62 int create_detached_tmpfs(void)
63 {
64 	int fd_context, fd_tmpfs;
65 
66 	fd_context = sys_fsopen("tmpfs", 0);
67 	if (fd_context < 0)
68 		return -1;
69 
70 	if (sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
71 		return -1;
72 
73 	fd_tmpfs = sys_fsmount(fd_context, 0, 0);
74 	close(fd_context);
75 	return fd_tmpfs;
76 }
77 
create_and_listen_unix_socket(const char * path)78 int create_and_listen_unix_socket(const char *path)
79 {
80 	struct sockaddr_un addr = {
81 		.sun_family = AF_UNIX,
82 	};
83 	assert(strlen(path) < sizeof(addr.sun_path) - 1);
84 	strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
85 	size_t addr_len =
86 		offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1;
87 	int fd, ret;
88 
89 	fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
90 	if (fd < 0)
91 		goto out;
92 
93 	ret = bind(fd, (const struct sockaddr *)&addr, addr_len);
94 	if (ret < 0)
95 		goto out;
96 
97 	ret = listen(fd, 128);
98 	if (ret < 0)
99 		goto out;
100 
101 	return fd;
102 
103 out:
104 	if (fd >= 0)
105 		close(fd);
106 	return -1;
107 }
108 
set_core_pattern(const char * pattern)109 bool set_core_pattern(const char *pattern)
110 {
111 	int fd;
112 	ssize_t ret;
113 
114 	fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC);
115 	if (fd < 0)
116 		return false;
117 
118 	ret = write(fd, pattern, strlen(pattern));
119 	close(fd);
120 	if (ret < 0)
121 		return false;
122 
123 	fprintf(stderr, "Set core_pattern to '%s' | %zu == %zu\n", pattern, ret, strlen(pattern));
124 	return ret == strlen(pattern);
125 }
126 
get_peer_pidfd(int fd)127 int get_peer_pidfd(int fd)
128 {
129 	int fd_peer_pidfd;
130 	socklen_t fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
131 	int ret = getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &fd_peer_pidfd,
132 			     &fd_peer_pidfd_len);
133 	if (ret < 0) {
134 		fprintf(stderr, "get_peer_pidfd: getsockopt(SO_PEERPIDFD) failed: %m\n");
135 		return -1;
136 	}
137 	fprintf(stderr, "get_peer_pidfd: successfully retrieved pidfd %d\n", fd_peer_pidfd);
138 	return fd_peer_pidfd;
139 }
140 
get_pidfd_info(int fd_peer_pidfd,struct pidfd_info * info)141 bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info)
142 {
143 	int ret;
144 	memset(info, 0, sizeof(*info));
145 	info->mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL;
146 	ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, info);
147 	if (ret < 0) {
148 		fprintf(stderr, "get_pidfd_info: ioctl(PIDFD_GET_INFO) failed: %m\n");
149 		return false;
150 	}
151 	fprintf(stderr, "get_pidfd_info: mask=0x%llx, coredump_mask=0x%x, coredump_signal=%d\n",
152 		(unsigned long long)info->mask, info->coredump_mask, info->coredump_signal);
153 	return true;
154 }
155 
156 /* Protocol helper functions */
157 
recv_marker(int fd)158 ssize_t recv_marker(int fd)
159 {
160 	enum coredump_mark mark = COREDUMP_MARK_REQACK;
161 	ssize_t ret;
162 
163 	ret = recv(fd, &mark, sizeof(mark), MSG_WAITALL);
164 	if (ret != sizeof(mark))
165 		return -1;
166 
167 	switch (mark) {
168 	case COREDUMP_MARK_REQACK:
169 		fprintf(stderr, "Received marker: ReqAck\n");
170 		return COREDUMP_MARK_REQACK;
171 	case COREDUMP_MARK_MINSIZE:
172 		fprintf(stderr, "Received marker: MinSize\n");
173 		return COREDUMP_MARK_MINSIZE;
174 	case COREDUMP_MARK_MAXSIZE:
175 		fprintf(stderr, "Received marker: MaxSize\n");
176 		return COREDUMP_MARK_MAXSIZE;
177 	case COREDUMP_MARK_UNSUPPORTED:
178 		fprintf(stderr, "Received marker: Unsupported\n");
179 		return COREDUMP_MARK_UNSUPPORTED;
180 	case COREDUMP_MARK_CONFLICTING:
181 		fprintf(stderr, "Received marker: Conflicting\n");
182 		return COREDUMP_MARK_CONFLICTING;
183 	default:
184 		fprintf(stderr, "Received unknown marker: %u\n", mark);
185 		break;
186 	}
187 	return -1;
188 }
189 
read_marker(int fd,enum coredump_mark mark)190 bool read_marker(int fd, enum coredump_mark mark)
191 {
192 	ssize_t ret;
193 
194 	ret = recv_marker(fd);
195 	if (ret < 0)
196 		return false;
197 	return ret == mark;
198 }
199 
read_coredump_req(int fd,struct coredump_req * req)200 bool read_coredump_req(int fd, struct coredump_req *req)
201 {
202 	ssize_t ret;
203 	size_t field_size, user_size, ack_size, kernel_size, remaining_size;
204 
205 	memset(req, 0, sizeof(*req));
206 	field_size = sizeof(req->size);
207 
208 	/* Peek the size of the coredump request. */
209 	ret = recv(fd, req, field_size, MSG_PEEK | MSG_WAITALL);
210 	if (ret != field_size) {
211 		fprintf(stderr, "read_coredump_req: peek failed (got %zd, expected %zu): %m\n",
212 			ret, field_size);
213 		return false;
214 	}
215 	kernel_size = req->size;
216 
217 	if (kernel_size < COREDUMP_ACK_SIZE_VER0) {
218 		fprintf(stderr, "read_coredump_req: kernel_size %zu < min %d\n",
219 			kernel_size, COREDUMP_ACK_SIZE_VER0);
220 		return false;
221 	}
222 	if (kernel_size >= PAGE_SIZE) {
223 		fprintf(stderr, "read_coredump_req: kernel_size %zu >= PAGE_SIZE %d\n",
224 			kernel_size, PAGE_SIZE);
225 		return false;
226 	}
227 
228 	/* Use the minimum of user and kernel size to read the full request. */
229 	user_size = sizeof(struct coredump_req);
230 	ack_size = user_size < kernel_size ? user_size : kernel_size;
231 	ret = recv(fd, req, ack_size, MSG_WAITALL);
232 	if (ret != ack_size)
233 		return false;
234 
235 	fprintf(stderr, "Read coredump request with size %u and mask 0x%llx\n",
236 		req->size, (unsigned long long)req->mask);
237 
238 	if (user_size > kernel_size)
239 		remaining_size = user_size - kernel_size;
240 	else
241 		remaining_size = kernel_size - user_size;
242 
243 	if (PAGE_SIZE <= remaining_size)
244 		return false;
245 
246 	/*
247 	 * Discard any additional data if the kernel's request was larger than
248 	 * what we knew about or cared about.
249 	 */
250 	if (remaining_size) {
251 		char buffer[PAGE_SIZE];
252 
253 		ret = recv(fd, buffer, sizeof(buffer), MSG_WAITALL);
254 		if (ret != remaining_size)
255 			return false;
256 		fprintf(stderr, "Discarded %zu bytes of data after coredump request\n", remaining_size);
257 	}
258 
259 	return true;
260 }
261 
send_coredump_ack(int fd,const struct coredump_req * req,__u64 mask,size_t size_ack)262 bool send_coredump_ack(int fd, const struct coredump_req *req,
263 		       __u64 mask, size_t size_ack)
264 {
265 	ssize_t ret;
266 	/*
267 	 * Wrap struct coredump_ack in a larger struct so we can
268 	 * simulate sending to much data to the kernel.
269 	 */
270 	struct large_ack_for_size_testing {
271 		struct coredump_ack ack;
272 		char buffer[PAGE_SIZE];
273 	} large_ack = {};
274 
275 	if (!size_ack)
276 		size_ack = sizeof(struct coredump_ack) < req->size_ack ?
277 				   sizeof(struct coredump_ack) :
278 				   req->size_ack;
279 	large_ack.ack.mask = mask;
280 	large_ack.ack.size = size_ack;
281 	ret = send(fd, &large_ack, size_ack, MSG_NOSIGNAL);
282 	if (ret != size_ack)
283 		return false;
284 
285 	fprintf(stderr, "Sent coredump ack with size %zu and mask 0x%llx\n",
286 		size_ack, (unsigned long long)mask);
287 	return true;
288 }
289 
check_coredump_req(const struct coredump_req * req,size_t min_size,__u64 required_mask)290 bool check_coredump_req(const struct coredump_req *req, size_t min_size,
291 			__u64 required_mask)
292 {
293 	if (req->size < min_size)
294 		return false;
295 	if ((req->mask & required_mask) != required_mask)
296 		return false;
297 	if (req->mask & ~required_mask)
298 		return false;
299 	return true;
300 }
301 
open_coredump_tmpfile(int fd_tmpfs_detached)302 int open_coredump_tmpfile(int fd_tmpfs_detached)
303 {
304 	return openat(fd_tmpfs_detached, ".", O_TMPFILE | O_RDWR | O_EXCL, 0600);
305 }
306 
process_coredump_worker(int fd_coredump,int fd_peer_pidfd,int fd_core_file)307 void process_coredump_worker(int fd_coredump, int fd_peer_pidfd, int fd_core_file)
308 {
309 	int epfd = -1;
310 	int exit_code = EXIT_FAILURE;
311 	struct epoll_event ev;
312 	int flags;
313 
314 	/* Set socket to non-blocking mode for edge-triggered epoll */
315 	flags = fcntl(fd_coredump, F_GETFL, 0);
316 	if (flags < 0) {
317 		fprintf(stderr, "Worker: fcntl(F_GETFL) failed: %m\n");
318 		goto out;
319 	}
320 	if (fcntl(fd_coredump, F_SETFL, flags | O_NONBLOCK) < 0) {
321 		fprintf(stderr, "Worker: fcntl(F_SETFL, O_NONBLOCK) failed: %m\n");
322 		goto out;
323 	}
324 
325 	epfd = epoll_create1(0);
326 	if (epfd < 0) {
327 		fprintf(stderr, "Worker: epoll_create1() failed: %m\n");
328 		goto out;
329 	}
330 
331 	ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
332 	ev.data.fd = fd_coredump;
333 	if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd_coredump, &ev) < 0) {
334 		fprintf(stderr, "Worker: epoll_ctl(EPOLL_CTL_ADD) failed: %m\n");
335 		goto out;
336 	}
337 
338 	for (;;) {
339 		struct epoll_event events[1];
340 		int n = epoll_wait(epfd, events, 1, -1);
341 		if (n < 0) {
342 			fprintf(stderr, "Worker: epoll_wait() failed: %m\n");
343 			break;
344 		}
345 
346 		if (events[0].events & (EPOLLIN | EPOLLRDHUP)) {
347 			for (;;) {
348 				char buffer[4096];
349 				ssize_t bytes_read = read(fd_coredump, buffer, sizeof(buffer));
350 				if (bytes_read < 0) {
351 					if (errno == EAGAIN || errno == EWOULDBLOCK)
352 						break;
353 					fprintf(stderr, "Worker: read() failed: %m\n");
354 					goto out;
355 				}
356 				if (bytes_read == 0)
357 					goto done;
358 				ssize_t bytes_write = write(fd_core_file, buffer, bytes_read);
359 				if (bytes_write != bytes_read) {
360 					if (bytes_write < 0 && errno == ENOSPC)
361 						continue;
362 					fprintf(stderr, "Worker: write() failed (read=%zd, write=%zd): %m\n",
363 						bytes_read, bytes_write);
364 					goto out;
365 				}
366 			}
367 		}
368 	}
369 
370 done:
371 	exit_code = EXIT_SUCCESS;
372 	fprintf(stderr, "Worker: completed successfully\n");
373 out:
374 	if (epfd >= 0)
375 		close(epfd);
376 	if (fd_core_file >= 0)
377 		close(fd_core_file);
378 	if (fd_peer_pidfd >= 0)
379 		close(fd_peer_pidfd);
380 	if (fd_coredump >= 0)
381 		close(fd_coredump);
382 	_exit(exit_code);
383 }
384