1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 /*
7  * Copyright (C) 2018-2022 Intel Corporation.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <sys/queue.h>
28 #include <pthread.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <string.h>
32 #include <limits.h>
33 #include <errno.h>
34 #include <malloc.h>
35 #include <event2/event.h>
36 #include <event2/listener.h>
37 #include <event2/thread.h>
38 #include <signal.h>
39 #include <stdint.h>
40 #include <sys/cdefs.h>
41 
42 #include "packet.h"
43 #include "protocol.h"
44 #include "log_sys.h"
45 #include "version.h"
46 #include "strutils.h"
47 
48 #define FILE_PATH_LEN_MAX 256
49 
50 /**
51  * Usercrash works as C/S model: usercrash_s works as usercrash server, which
52  * is to handle events from client in endless loop. Once server receives events
53  * from client, it will create usercrash_0x file under /var/log/usercrashes/
54  * and send file fd to client. Then server will wait for client filling the
55  * event info completely to the crash file. After client's work has been done,
56  * server will be responsiable to free the crash node and process other events.
57  */
58 
59 struct crash_node {
60 	int crash_fd;
61 	int pid;
62 	int out_fd;
63 	char name[COMM_NAME_LEN];
64 	struct event *crash_event;
65 	char crash_path[FILE_PATH_LEN_MAX];
66 
67 	TAILQ_ENTRY(crash_node) entries;
68 };
69 
70 static const char usercrash_directory[] = "/var/log/usercrashes";
71 static int usercrash_count = 50;
72 static int next_usercrash;
73 
74 static int kMaxConcurrentDumps = 1;
75 static int num_concurrent_dumps;
76 TAILQ_HEAD(, crash_node) queue_t;
77 static pthread_mutex_t queue_mtx = PTHREAD_MUTEX_INITIALIZER;
78 
79 
80 static void push_back(struct crash_node *crash);
81 static struct crash_node *pop_front(void);
82 
83 /* Forward declare the callbacks so they can be placed in a sensible order */
84 static void crash_accept_cb(struct evconnlistener *listener,
85 		evutil_socket_t sockfd, struct sockaddr*, int, void*);
86 static void crash_request_cb(evutil_socket_t sockfd, short ev, void *arg);
87 static void crash_completed_cb(evutil_socket_t sockfd, short ev, void *arg);
88 
debuggerd_register_handlers(struct sigaction * action)89 static void __attribute__((__unused__)) debuggerd_register_handlers(
90 			struct sigaction *action)
91 {
92 	sigaction(SIGABRT, action, NULL);
93 	sigaction(SIGBUS, action, NULL);
94 	sigaction(SIGFPE, action, NULL);
95 	sigaction(SIGILL, action, NULL);
96 	sigaction(SIGSEGV, action, NULL);
97 #if defined(SIGSTKFLT)
98 	sigaction(SIGSTKFLT, action, NULL);
99 #endif
100 	sigaction(SIGSYS, action, NULL);
101 	sigaction(SIGTRAP, action, NULL);
102 }
103 
free_crash(struct crash_node * crash)104 static void free_crash(struct crash_node *crash)
105 {
106 	event_free(crash->crash_event);
107 	close(crash->crash_fd);
108 	free(crash);
109 }
110 
push_back(struct crash_node * crash)111 static void push_back(struct crash_node *crash)
112 {
113 	pthread_mutex_lock(&queue_mtx);
114 	TAILQ_INSERT_TAIL(&queue_t, crash, entries);
115 	pthread_mutex_unlock(&queue_mtx);
116 }
117 
pop_front(void)118 static struct crash_node *pop_front(void)
119 {
120 	struct crash_node *crash = NULL;
121 
122 	pthread_mutex_lock(&queue_mtx);
123 	if (!TAILQ_EMPTY(&queue_t)) {
124 		crash = TAILQ_FIRST(&queue_t);
125 		TAILQ_REMOVE(&queue_t, crash, entries);
126 	}
127 	pthread_mutex_unlock(&queue_mtx);
128 
129 	return crash;
130 }
131 
find_oldest_usercrash(void)132 static void find_oldest_usercrash(void)
133 {
134 	int i;
135 	int len;
136 	int oldest_usercrash = 0;
137 	time_t oldest_time = LONG_MAX;
138 	char path[FILE_PATH_LEN_MAX];
139 	struct stat st;
140 
141 	memset(path, 0, FILE_PATH_LEN_MAX);
142 	for (i = 0; i < usercrash_count; ++i) {
143 		len = snprintf(path, sizeof(path), "%s/usercrash_%02d",
144 				usercrash_directory, i);
145 		if (s_not_expect(len, sizeof(path))) {
146 			LOGE("failed to generate path\n");
147 			continue;
148 		}
149 		if (stat(path, &st) != 0) {
150 			if (errno == ENOENT) {
151 				oldest_usercrash = i;
152 				break;
153 			}
154 
155 			LOGE("failed to stat %s\n", path);
156 			continue;
157 		}
158 
159 		if (st.st_mtime < oldest_time) {
160 			oldest_usercrash = i;
161 			oldest_time = st.st_mtime;
162 		}
163 	}
164 	next_usercrash = oldest_usercrash;
165 }
166 
get_usercrash(struct crash_node * crash)167 static int get_usercrash(struct crash_node *crash)
168 {
169 	/**
170 	 * If kMaxConcurrentDumps is greater than 1, then theoretically the
171 	 * same filename could be handed out to multiple processes. Unlink and
172 	 * create the file, instead of using O_TRUNC, to avoid two processes
173 	 * interleaving their output
174 	 */
175 	int result;
176 	int len;
177 	char file_name[FILE_PATH_LEN_MAX];
178 
179 	memset(file_name, 0, FILE_PATH_LEN_MAX);
180 	len = snprintf(file_name, sizeof(file_name), "%s/usercrash_%02d",
181 				usercrash_directory, next_usercrash);
182 	if (s_not_expect(len, sizeof(file_name))) {
183 		LOGE("failed to generate file name\n");
184 		return -1;
185 	}
186 
187 	if (unlink(file_name) != 0 && errno != ENOENT) {
188 		LOGE("failed to unlink usercrash at %s\n", file_name);
189 		return -1;
190 	}
191 	result = open(file_name, O_CREAT | O_WRONLY, 0644);
192 	if (result == -1) {
193 		LOGE("failed to create usercrash at %s\n", file_name);
194 		return -1;
195 
196 	}
197 	next_usercrash = (next_usercrash + 1) % usercrash_count;
198 	crash->out_fd = result;
199 	strncpy(crash->crash_path, file_name, FILE_PATH_LEN_MAX);
200 	crash->crash_path[FILE_PATH_LEN_MAX - 1] = '\0';
201 	return 0;
202 }
203 
perform_request(struct crash_node * crash)204 static void perform_request(struct crash_node *crash)
205 {
206 	ssize_t rc;
207 	struct crash_packet response = {0};
208 
209 	if (get_usercrash(crash)) {
210 		LOGE("server exit for open usercrash file failed\n");
211 		exit(EXIT_FAILURE);
212 	}
213 
214 	LOGI("Prepare to write the '%s' log to %s\n",
215 				crash->name, crash->crash_path);
216 	response.packet_type = kPerformDump;
217 	rc = send_fd(crash->crash_fd, &response,
218 				sizeof(response), crash->out_fd);
219 	close(crash->out_fd);
220 	if (rc == -1) {
221 		LOGE("failed to send response to CrashRequest\n");
222 		goto fail;
223 	} else if (rc != sizeof(response)) {
224 		LOGE("crash socket write returned short\n");
225 		goto fail;
226 	} else {
227 		struct timeval timeout = { 100, 0 };
228 		struct event_base *base = event_get_base(crash->crash_event);
229 
230 		event_assign(crash->crash_event, base, crash->crash_fd,
231 				EV_TIMEOUT | EV_READ,
232 				crash_completed_cb, crash);
233 		event_add(crash->crash_event, &timeout);
234 	}
235 
236 	++num_concurrent_dumps;
237 	return;
238 
239 fail:
240 	free_crash(crash);
241 }
242 
dequeue_requests(void)243 static void dequeue_requests(void)
244 {
245 	while (!TAILQ_EMPTY(&queue_t) &&
246 				num_concurrent_dumps < kMaxConcurrentDumps) {
247 		struct crash_node *next_crash = pop_front();
248 
249 		perform_request(next_crash);
250 	}
251 }
252 
crash_accept_cb(struct evconnlistener * listener,evutil_socket_t sockfd,struct sockaddr * sa,int socklen,void * user_data)253 static void crash_accept_cb(struct evconnlistener *listener,
254 			evutil_socket_t sockfd,
255 		struct sockaddr *sa __attribute__((unused)),
256 		int socklen __attribute__((unused)),
257 		void *user_data __attribute__((unused)))
258 {
259 	struct event_base *base = evconnlistener_get_base(listener);
260 	struct crash_node *crash = (struct crash_node *)malloc(
261 					sizeof(struct crash_node));
262 
263 	struct timeval timeout = { 1, 0 };
264 	struct event *crash_event = event_new(base, sockfd,
265 			EV_TIMEOUT | EV_READ, crash_request_cb, crash);
266 
267 	if (!crash) {
268 		LOGE("Malloc memory for crash failed.\n");
269 		exit(EXIT_FAILURE);
270 	}
271 
272 	memset(crash, 0, sizeof(struct crash_node));
273 	crash->crash_fd = sockfd;
274 	crash->crash_event = crash_event;
275 	event_add(crash_event, &timeout);
276 }
277 
crash_request_cb(evutil_socket_t sockfd,short ev,void * arg)278 static void crash_request_cb(evutil_socket_t sockfd, short ev, void *arg)
279 {
280 	ssize_t rc;
281 	struct crash_node *crash = arg;
282 	struct crash_packet request = {0};
283 
284 	if ((ev & EV_TIMEOUT) != 0) {
285 		LOGE("crash request timed out\n");
286 		goto fail;
287 	} else if ((ev & EV_READ) == 0) {
288 		LOGE("usercrash server received unexpected event ");
289 		LOGE("from crash socket\n");
290 		goto fail;
291 	}
292 
293 	rc = read(sockfd, &request, sizeof(request));
294 	if (rc == -1) {
295 		LOGE("failed to read from crash socket\n");
296 		goto fail;
297 	} else if (rc != sizeof(request)) {
298 		LOGE("crash socket received short read of length %lu ", rc);
299 		LOGE("(expected %lu)\n", sizeof(request));
300 		goto fail;
301 	}
302 
303 	if (request.packet_type != kDumpRequest) {
304 		LOGE("unexpected crash packet type, expected kDumpRequest, ");
305 		LOGE("received %d\n", request.packet_type);
306 		goto fail;
307 	}
308 	crash->pid = request.pid;
309 	strncpy(crash->name, request.name, COMM_NAME_LEN);
310 	crash->name[COMM_NAME_LEN - 1] = '\0';
311 	LOGI("received crash request from pid %d, name: %s\n",
312 				crash->pid, crash->name);
313 
314 	if (num_concurrent_dumps == kMaxConcurrentDumps) {
315 		LOGI("enqueueing crash request for pid %d\n", crash->pid);
316 		push_back(crash);
317 	} else {
318 		perform_request(crash);
319 	}
320 
321 	return;
322 
323 fail:
324 	free_crash(crash);
325 }
326 
crash_completed_cb(evutil_socket_t sockfd,short ev,void * arg)327 static void crash_completed_cb(evutil_socket_t sockfd, short ev, void *arg)
328 {
329 	ssize_t rc;
330 	struct crash_node *crash = arg;
331 	struct crash_packet request = {0};
332 
333 	--num_concurrent_dumps;
334 
335 	if ((ev & EV_TIMEOUT) != 0) {
336 		LOGE("error for crash request timed out, error (%s)\n",
337 			strerror(errno));
338 		goto out;
339 	} else if ((ev & EV_READ) == 0) {
340 		LOGE("usercrash server received unexpected event ");
341 		LOGE("from crash socket\n");
342 		goto out;
343 	}
344 
345 	rc = read(sockfd, &request, sizeof(request));
346 	if (rc == -1) {
347 		LOGE("failed to read from crash socket\n");
348 		goto out;
349 	} else if (rc != sizeof(request)) {
350 		LOGE("crash socket received short read of length %lu, ", rc);
351 		LOGE("(expected %lu)\n", sizeof(request));
352 		goto out;
353 	}
354 
355 	if (request.packet_type != kCompletedDump) {
356 		LOGE("unexpected crash packet type, expected kCompletedDump, ");
357 		LOGE("received %d\n", request.packet_type);
358 		goto out;
359 	}
360 
361 out:
362 	free_crash(crash);
363 	/* If there's something queued up, let them proceed */
364 	dequeue_requests();
365 }
366 
sig_handler(int sig)367 static void sig_handler(int sig)
368 {
369 	LOGE("received fatal signal: %d\n", sig);
370 	exit(EXIT_FAILURE);
371 }
372 
print_usage(void)373 static void print_usage(void)
374 {
375 	printf("usercrash - tool to dump crash info for the crashes in the ");
376 	printf("userspace on sos. It's the server role of usercrash.\n");
377 	printf("[Usage] usercrash_s (root role to run)\n");
378 	printf("[Option]\n");
379 	printf("\t-h: print this usage message\n");
380 	printf("\t-v: print usercrash_s version\n");
381 	printf("[Output] crash log is in %s folder\n", usercrash_directory);
382 }
383 
main(int argc,char * argv[])384 int main(int argc, char *argv[])
385 {
386 	char socket_path[SOCKET_PATH_MAX];
387 	DIR *dir;
388 	int fd;
389 	int len;
390 	evutil_socket_t crash_socket;
391 	int opt;
392 	struct sigaction action;
393 	struct event_base *base;
394 	struct evconnlistener *listener;
395 
396 	if (argc > 1) {
397 		while ((opt = getopt(argc, argv, "vh")) != -1) {
398 			switch (opt) {
399 			case 'v':
400 				printf("version is %d.%d-%s, build by %s@%s\n",
401 					UC_MAJOR_VERSION, UC_MINOR_VERSION,
402 					UC_BUILD_VERSION, UC_BUILD_USER,
403 					UC_BUILD_TIME);
404 				break;
405 			case 'h':
406 				print_usage();
407 				break;
408 			default:
409 				printf("unknown option\n");
410 				exit(EXIT_FAILURE);
411 			}
412 		}
413 		return 0;
414 	}
415 
416 	if (getuid() != 0) {
417 		LOGE("failed to boot usercrash_s, root is required\n");
418 		exit(EXIT_FAILURE);
419 	}
420 
421 	dir = opendir(usercrash_directory);
422 	if (dir == NULL) {
423 		fd = mkdir(usercrash_directory, 0755);
424 		if (fd == -1) {
425 			LOGE("create log folder failed, error (%s)\n",
426 					strerror(errno));
427 			exit(EXIT_FAILURE);
428 		}
429 		if (chmod(usercrash_directory, 0755) == -1) {
430 			LOGE("failed to change usercrash folder priority, ");
431 			LOGE("error (%s)\n", strerror(errno));
432 			exit(EXIT_FAILURE);
433 		}
434 	} else {
435 		closedir(dir);
436 	}
437 
438 	/* Don't try to connect to ourselves if we crash */
439 	memset(&action, 0, sizeof(struct sigaction));
440 	action.sa_handler = sig_handler;
441 	debuggerd_register_handlers(&action);
442 
443 	find_oldest_usercrash();
444 	len = snprintf(socket_path, sizeof(socket_path), "%s/%s",
445 			RESERVED_SOCKET_PREFIX, SOCKET_NAME);
446 	if (s_not_expect(len , sizeof(socket_path))) {
447 		LOGE("construct socket path error\n");
448 		exit(EXIT_FAILURE);
449 	}
450 	/**
451 	 * evutil_socket_t on other platform except WIN32 platform is int
452 	 * type, but on WIN32 platform evutil_socket_t is intptr_t type.
453 	 * So, considering compatibility, here need to transfer socket fd to
454 	 * evutil_socket_t type.
455 	 */
456 	crash_socket = (evutil_socket_t)create_socket_server(socket_path,
457 			SOCK_SEQPACKET);
458 
459 	if (crash_socket == -1) {
460 		LOGE("failed to get socket from init, error (%s)\n",
461 					strerror(errno));
462 		exit(EXIT_FAILURE);
463 	}
464 	if (chmod(socket_path, 0622) == -1) {
465 		LOGE("failed to change usercrashd_crash priority\n");
466 		goto fail;
467 	}
468 
469 	evutil_make_socket_nonblocking(crash_socket);
470 
471 	base = event_base_new();
472 	if (!base) {
473 		LOGE("failed to create event_base\n");
474 		goto fail;
475 	}
476 
477 	TAILQ_INIT(&queue_t);
478 	listener = evconnlistener_new(base, crash_accept_cb, NULL,
479 			LEV_OPT_CLOSE_ON_FREE, -1, crash_socket);
480 	if (!listener) {
481 		LOGE("failed to create evconnlistener: %s\n", strerror(errno));
482 		goto fail;
483 	}
484 
485 	LOGI("usercrash_s successfully initialized\n");
486 	event_base_dispatch(base);
487 
488 	evconnlistener_free(listener);
489 	event_base_free(base);
490 
491 	close(crash_socket);
492 	return 0;
493 
494 fail:
495 	close(crash_socket);
496 	exit(EXIT_FAILURE);
497 }
498