1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <stdarg.h>
12 #include "log_sys.h"
13 #include "crash_dump.h"
14 #include "cmdutils.h"
15 #include "fsutils.h"
16 #include "strutils.h"
17 
18 #define GET_TID "/proc/%d/status"
19 #define GET_COMM "/proc/%d/exe"
20 #define GET_K_STACK "/proc/%d/stack"
21 #define GET_MAPS "/proc/%d/maps"
22 #define GET_OPEN_FILES "/proc/%d/fd"
23 #define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
24 #define DUMP_FILE "/tmp/core"
25 #define BUFFER_SIZE 8196
26 #define LINK_LEN 512
27 #define TID "Tgid:"
28 /* 128 means the length of the DUMP_FILE */
29 #define FORMAT_LENGTH (LINK_LEN + 128)
30 
loginfo(int fd,const char * fmt,...)31 static void loginfo(int fd, const char *fmt, ...)
32 {
33 	char *buf;
34 	va_list ap;
35 	int len, ret;
36 
37 	va_start(ap, fmt);
38 	len = vasprintf(&buf, fmt, ap);
39 	va_end(ap);
40 	if (len == -1) {
41 		LOGE("write to buf failed\n");
42 		return;
43 	}
44 
45 	ret = write(fd, buf, len);
46 	if (ret != len) {
47 		LOGE("write in loginfo failed\n");
48 	}
49 	free(buf);
50 }
51 
get_signame(int sig)52 static const char *get_signame(int sig)
53 {
54 	switch (sig) {
55 	case SIGABRT:
56 		return "SIGABRT";
57 	case SIGBUS:
58 		return "SIGBUS";
59 	case SIGFPE:
60 		return "SIGFPE";
61 	case SIGILL:
62 		return "SIGILL";
63 	case SIGSEGV:
64 		return "SIGSEGV";
65 #if defined(SIGSTKFLT)
66 	case SIGSTKFLT:
67 		return "SIGSTKFLT";
68 #endif
69 	case SIGSTOP:
70 		return "SIGSTOP";
71 	case SIGSYS:
72 		return "SIGSYS";
73 	case SIGTRAP:
74 		return "SIGTRAP";
75 	case DEBUGGER_SIGNAL:
76 		return "<debuggerd signal>";
77 	default:
78 		return "?";
79 	}
80 }
81 
82 /**
83  * Save core dump to file.
84  * @filename: core dump file name
85  * return 0 on success, or -1 on error
86  */
save_coredump(const char * filename)87 static int save_coredump(const char *filename)
88 {
89 	size_t read_len;
90 	size_t write_len;
91 	char input_buffer[BUFFER_SIZE];
92 	FILE *dump_file;
93 
94 	/* open dump file in write and binary mode */
95 	dump_file = fopen(filename, "wb");
96 	if (dump_file == NULL) {
97 		LOGE("fopen core file failed\n");
98 		return -1;
99 	}
100 	/* Read from STDIN and write to dump file */
101 	while (1) {
102 		read_len = fread(input_buffer, 1, BUFFER_SIZE, stdin);
103 		if (read_len == 0)
104 			break;
105 		write_len = fwrite(input_buffer, 1, read_len, dump_file);
106 		if (write_len == 0) {
107 			LOGE("fwrite error\n");
108 			fclose(dump_file);
109 			return -1;
110 		}
111 	}
112 
113 	fclose(dump_file);
114 	return 0;
115 }
116 
get_backtrace(int pid,int fd,int sig,const char * comm)117 static int get_backtrace(int pid, int fd, int sig, const char *comm)
118 {
119 	char *membkt;
120 	char format[FORMAT_LENGTH];
121 	size_t len, ret;
122 	int flen;
123 
124 	loginfo(fd, "\nBackTrace:\n\n");
125 	memset(format, 0, sizeof(format));
126 	if (sig == DEBUGGER_SIGNAL) {
127 		flen = snprintf(format, sizeof(format), "-p %d", pid);
128 	} else {
129 		flen = snprintf(format, sizeof(format), "%s %s", comm,
130 				DUMP_FILE);
131 		if (save_coredump(DUMP_FILE) == -1) {
132 			LOGE("save core file failed\n");
133 			return -1;
134 		}
135 	}
136 	if (s_not_expect(flen, sizeof(format))) {
137 		LOGE("failed to generate format\n");
138 		return -1;
139 	}
140 	len = exec_out2mem(&membkt, GET_GDB_INFO, format);
141 	if (len <= 0) {
142 		LOGE("get gdb info failed\n");
143 		return -1;
144 	}
145 	ret = write(fd, membkt, len);
146 	free(membkt);
147 	if (ret != len) {
148 		LOGE("write file failed\n");
149 		return -1;
150 	}
151 	return 0;
152 
153 }
154 
155 /**
156  * Save process proc info to file.
157  * @pid: process pid
158  * @fd: usercrash file fd
159  * @path: process proc path
160  * @name: a string save to file
161  * return 0 on success, or -1 on error
162  */
save_proc_info(int pid,int fd,const char * path,const char * name)163 static int save_proc_info(int pid, int fd, const char *path, const char *name)
164 {
165 	int ret;
166 	char *data;
167 	char format[128];
168 	unsigned long size;
169 
170 	loginfo(fd, "\n%s:\n\n", name);
171 	memset(format, 0, sizeof(format));
172 	ret = snprintf(format, sizeof(format), path, pid);
173 	if (s_not_expect(ret, sizeof(format))) {
174 		LOGE("failed to generate format");
175 		return -1;
176 	}
177 	ret = read_file(format, &size, (void *)&data);
178 	if (ret) {
179 		LOGE("read file failed\n");
180 		return -1;
181 	}
182 	ret = write(fd, data, size);
183 	if ((unsigned long)ret != size) {
184 		LOGE("write file failed\n");
185 		return -1;
186 	}
187 	free(data);
188 	return 0;
189 
190 }
191 
get_openfiles(int pid,int fd,const char * path,const char * name)192 static int get_openfiles(int pid, int fd, const char *path, const char *name)
193 {
194 	int ret;
195 	int loop;
196 	int fdcount;
197 	char *fdname;
198 	char format[128];
199 	char *files[256];
200 	char linkname[LINK_LEN];
201 
202 	loginfo(fd, "\n%s:\n\n", name);
203 	memset(format, 0, sizeof(format));
204 	ret = snprintf(format, sizeof(format), path, pid);
205 	if (s_not_expect(ret, sizeof(format))) {
206 		LOGE("failed to generate format");
207 		return -1;
208 	}
209 	fdcount = lsdir(format, files, ARRAY_SIZE(files));
210 	if (fdcount < 0) {
211 		LOGE("get fd list failed\n");
212 		return -1;
213 	}
214 	for (loop = 2; loop < fdcount; loop++) {
215 		memset(linkname, 0, LINK_LEN);
216 		ret = readlink(files[loop], linkname, LINK_LEN);
217 		if (ret < 0 || ret >= LINK_LEN) {
218 			LOGE("get fd link fd failed\n");
219 			continue;
220 		}
221 		fdname = strrchr(files[loop], '/') + 1;
222 		loginfo(fd, "%s -> %s\n", fdname, linkname);
223 	}
224 	for (loop = 0; loop < fdcount; loop++)
225 		free(files[loop]);
226 
227 	return 0;
228 }
229 
save_usercrash_file(int pid,int tgid,const char * comm,int sig,int out_fd)230 static int save_usercrash_file(int pid, int tgid, const char *comm,
231 		int sig, int out_fd)
232 {
233 	int ret;
234 
235 	loginfo(out_fd, "*** *** *** *** *** *** *** *** *** *** *** *** *** ");
236 	loginfo(out_fd, "*** *** ***\n");
237 	loginfo(out_fd, "pid: %d, tgid: %d, comm: %s\n\n\n", pid, tgid, comm);
238 	loginfo(out_fd, "signal: %d (%s)\n", sig, get_signame(sig));
239 
240 	ret = save_proc_info(pid, out_fd, GET_K_STACK, "Stack");
241 	if (ret) {
242 		LOGE("save stack failed\n");
243 		return -1;
244 	}
245 
246 	ret = save_proc_info(pid, out_fd, GET_MAPS, "Maps");
247 	if (ret) {
248 		LOGE("save maps failed\n");
249 		return -1;
250 	}
251 
252 	ret = get_openfiles(pid, out_fd, GET_OPEN_FILES, "Open files");
253 	if (ret) {
254 		LOGE("save openfiles failed\n");
255 		return -1;
256 	}
257 
258 	ret = get_backtrace(pid, out_fd, sig, comm);
259 	if (ret) {
260 		LOGE("save backtrace failed\n");
261 		return -1;
262 	}
263 
264 	return 0;
265 }
266 
get_key_value(int pid,const char * path,const char * key,const size_t klen,char * value,const size_t value_len)267 static int get_key_value(int pid, const char *path, const char *key,
268 		const size_t klen, char *value, const size_t value_len)
269 {
270 	int len;
271 	int ret;
272 	unsigned long size;
273 	char *data;
274 	char *msg;
275 	char *start;
276 	char *end;
277 	char format[128];
278 
279 	memset(format, 0, sizeof(format));
280 	ret = snprintf(format, sizeof(format), path, pid);
281 	if (s_not_expect(ret, sizeof(format))) {
282 		LOGE("failed to generate format");
283 		return -1;
284 	}
285 	ret = read_file(format, &size, (void *)&data);
286 	if (ret || !data) {
287 		LOGE("read file failed\n");
288 		return -1;
289 	}
290 	msg = strstr(data, key);
291 	if (!msg) {
292 		LOGE("find key:%s failed\n", key);
293 		free(data);
294 		return -1;
295 	}
296 	end = strchr(msg, '\n');
297 	if (end == NULL)
298 		end = data + size;
299 	start = msg + klen;
300 	len = end - start;
301 	if (len >= (int)value_len) {
302 		free(data);
303 		return -1;
304 	}
305 	memcpy(value, start, len);
306 	*(value + len) = 0;
307 	free(data);
308 
309 	return 0;
310 }
311 
312 /**
313  * Get and save process info to out_fd.
314  * @pid: process pid
315  * @sig: signal from core dump
316  * @out_fd: file fd to save info
317  */
crash_dump(int pid,int sig,int out_fd)318 void crash_dump(int pid, int sig, int out_fd)
319 {
320 	int tgid;
321 	int ret;
322 	char comm[LINK_LEN];
323 	char result[16];
324 	char format[128];
325 
326 	memset(format, 0, sizeof(format));
327 	ret = snprintf(format, sizeof(format), GET_COMM, pid);
328 	if (s_not_expect(ret, sizeof(format))) {
329 		LOGE("failed to generate format\n");
330 		return;
331 	}
332 	ret = readlink(format, comm, LINK_LEN);
333 	if (ret < 0 || ret >= LINK_LEN) {
334 		LOGE("get process exe link failed\n");
335 		return;
336 	}
337 	comm[ret] = '\0';
338 
339 	ret = get_key_value(pid, GET_TID, TID, strlen(TID),
340 			result, sizeof(result));
341 	if (ret) {
342 		LOGE("get Tgid error\n");
343 		return;
344 	}
345 	tgid = (int)strtol(result, NULL, 10);
346 	if (!sig)
347 		sig = DEBUGGER_SIGNAL;
348 	if (save_usercrash_file(pid, tgid, comm, sig, out_fd))
349 		LOGE("dump log file failed\n");
350 }
351