1
2 /*
3 * Copyright (c) 2021 Huawei Device Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <pthread.h>
19 #include <sched.h>
20 #include <signal.h>
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <sys/capability.h>
25 #include <sys/mman.h>
26 #include <sys/prctl.h>
27 #include <sys/syscall.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <sys/uio.h>
31 #include <sys/wait.h>
32 #include <linux/futex.h>
33
34 #include <libunwind.h>
35 #include <libunwind_i-ohos.h>
36
37 #include "beget_ext.h"
38 #include "securec.h"
39 #include "init_cmds.h"
40 #include "init_log.h"
41 #include "crash_handler.h"
42
43 #define BEGET_DFX_CHECK(fd, retCode, exper, fmt, ...) \
44 if (!(retCode)) { \
45 PrintLog(fd, fmt, ##__VA_ARGS__); \
46 BEGET_LOGE(fmt, ##__VA_ARGS__); \
47 exper; \
48 }
49
50 static unw_addr_space_t g_addrSpace = NULL;
51 static void *g_reservedChildStack = NULL;
52 static ProcessDumpRequest g_request;
53 static const SignalInfo g_platformSignals[] = {
54 { SIGABRT, "SIGABRT" },
55 { SIGBUS, "SIGBUS" },
56 { SIGFPE, "SIGFPE" },
57 { SIGILL, "SIGILL" },
58 { SIGSEGV, "SIGSEGV" },
59 #if defined(SIGSTKFLT)
60 { SIGSTKFLT, "SIGSTKFLT" },
61 #endif
62 { SIGSYS, "SIGSYS" },
63 { SIGTRAP, "SIGTRAP" },
64 };
65
PrintLog(int fd,const char * format,...)66 __attribute__((noinline)) void PrintLog(int fd, const char *format, ...)
67 {
68 char buf[BUF_SZ] = {0};
69 va_list args;
70 va_start(args, format);
71 int size = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, format, args);
72 if (size == -1) {
73 BEGET_LOGE("Failed to sprintf %s", format);
74 va_end(args);
75 return;
76 }
77 va_end(args);
78 if (fd < 0) {
79 return;
80 }
81 (void)write(fd, buf, strlen(buf));
82 }
83
SetRegister(unw_context_t * context,const ucontext_t * uc)84 static int SetRegister(unw_context_t *context, const ucontext_t *uc)
85 {
86 #if defined(__arm__)
87 (void)memset_s(context, sizeof(*context), 0, sizeof(*context));
88 context->regs[ARM_R0] = uc->uc_mcontext.arm_r0;
89 context->regs[ARM_R1] = uc->uc_mcontext.arm_r1;
90 context->regs[ARM_R2] = uc->uc_mcontext.arm_r2;
91 context->regs[ARM_R3] = uc->uc_mcontext.arm_r3;
92 context->regs[ARM_R4] = uc->uc_mcontext.arm_r4;
93 context->regs[ARM_R5] = uc->uc_mcontext.arm_r5;
94 context->regs[ARM_R6] = uc->uc_mcontext.arm_r6;
95 context->regs[ARM_R7] = uc->uc_mcontext.arm_r7;
96 context->regs[ARM_R8] = uc->uc_mcontext.arm_r8;
97 context->regs[ARM_R9] = uc->uc_mcontext.arm_r9;
98 context->regs[ARM_R10] = uc->uc_mcontext.arm_r10;
99 context->regs[ARM_FP] = uc->uc_mcontext.arm_fp;
100 context->regs[ARM_IP] = uc->uc_mcontext.arm_ip;
101 context->regs[ARM_SP] = uc->uc_mcontext.arm_sp;
102 context->regs[ARM_LR] = uc->uc_mcontext.arm_lr;
103 context->regs[ARM_PC] = uc->uc_mcontext.arm_pc;
104 BEGET_LOGE("fp:%08x ip:%08x sp:%08x lr:%08x pc:%08x\n",
105 uc->uc_mcontext.arm_fp, uc->uc_mcontext.arm_ip, uc->uc_mcontext.arm_sp,
106 uc->uc_mcontext.arm_lr, uc->uc_mcontext.arm_pc);
107 #elif defined(__aarch64__)
108 // the ucontext.uc_mcontext.__reserved of libunwind is simplified with the system's own in aarch64
109 if (memcpy_s(context, sizeof(unw_context_t), uc, sizeof(unw_context_t)) != 0) {
110 return -1;
111 }
112 #endif
113 return 0;
114 }
115
PrintfRegister(const unw_context_t * context,int fd)116 static void PrintfRegister(const unw_context_t *context, int fd)
117 {
118 PrintLog(fd, "Registers:\n");
119 #if defined(__arm__)
120 PrintLog(fd, "r0:%08x r1:%08x r2:%08x r3:%08x\n",
121 context->regs[ARM_R0], context->regs[ARM_R1], context->regs[ARM_R2], context->regs[ARM_R3]);
122 PrintLog(fd, "r4:%08x r5:%08x r6:%08x r7:%08x\n",
123 context->regs[ARM_R4], context->regs[ARM_R5], context->regs[ARM_R6], context->regs[ARM_R7]);
124 PrintLog(fd, "r8:%08x r9:%08x r10:%08x\n",
125 context->regs[ARM_R8], context->regs[ARM_R9], context->regs[ARM_R10]);
126 PrintLog(fd, "fp:%08x ip:%08x sp:%08x lr:%08x pc:%08x\n",
127 context->regs[ARM_FP], context->regs[ARM_IP], context->regs[ARM_SP],
128 context->regs[ARM_LR], context->regs[ARM_PC]);
129 #elif defined(__aarch64__)
130 PrintLog(fd, "x0:%016lx x1:%016lx x2:%016lx x3:%016lx\n", \
131 context->uc_mcontext.regs[AARCH64_X0], context->uc_mcontext.regs[AARCH64_X1],
132 context->uc_mcontext.regs[AARCH64_X2], context->uc_mcontext.regs[AARCH64_X3]);
133 PrintLog(fd, "x4:%016lx x5:%016lx x6:%016lx x7:%016lx\n", \
134 context->uc_mcontext.regs[AARCH64_X4], context->uc_mcontext.regs[AARCH64_X5],
135 context->uc_mcontext.regs[AARCH64_X6], context->uc_mcontext.regs[AARCH64_X7]);
136 PrintLog(fd, "x8:%016lx x9:%016lx x10:%016lx x11:%016lx\n", \
137 context->uc_mcontext.regs[AARCH64_X8], context->uc_mcontext.regs[AARCH64_X9],
138 context->uc_mcontext.regs[AARCH64_X10], context->uc_mcontext.regs[AARCH64_X11]);
139 PrintLog(fd, "x12:%016lx x13:%016lx x14:%016lx x15:%016lx\n", \
140 context->uc_mcontext.regs[AARCH64_X12], context->uc_mcontext.regs[AARCH64_X13],
141 context->uc_mcontext.regs[AARCH64_X14], context->uc_mcontext.regs[AARCH64_X15]);
142 PrintLog(fd, "x16:%016lx x17:%016lx x18:%016lx x19:%016lx\n", \
143 context->uc_mcontext.regs[AARCH64_X16], context->uc_mcontext.regs[AARCH64_X17],
144 context->uc_mcontext.regs[AARCH64_X18], context->uc_mcontext.regs[AARCH64_X19]);
145 PrintLog(fd, "x20:%016lx x21:%016lx x22:%016lx x23:%016lx\n", \
146 context->uc_mcontext.regs[AARCH64_X20], context->uc_mcontext.regs[AARCH64_X21],
147 context->uc_mcontext.regs[AARCH64_X22], context->uc_mcontext.regs[AARCH64_X23]);
148 PrintLog(fd, "x24:%016lx x25:%016lx x26:%016lx x27:%016lx\n", \
149 context->uc_mcontext.regs[AARCH64_X24], context->uc_mcontext.regs[AARCH64_X25],
150 context->uc_mcontext.regs[AARCH64_X26], context->uc_mcontext.regs[AARCH64_X27]);
151 PrintLog(fd, "x28:%016lx x29:%016lx\n", \
152 context->uc_mcontext.regs[AARCH64_X28], context->uc_mcontext.regs[AARCH64_X29]);
153 PrintLog(fd, "lr:%016lx sp:%016lx pc:%016lx\n", \
154 context->uc_mcontext.regs[AARCH64_X30], context->uc_mcontext.sp, context->uc_mcontext.pc);
155 #endif
156 }
157
GetTimeMilliseconds(void)158 static uint64_t GetTimeMilliseconds(void)
159 {
160 struct timeval time;
161 gettimeofday(&time, NULL);
162 return ((uint64_t)time.tv_sec * 1000) + // 1000 : second to millisecond convert ratio
163 (((uint64_t)time.tv_usec) / 1000); // 1000 : microsecond to millisecond convert ratio
164 }
165
GetSignalName(const int32_t signal)166 const char *GetSignalName(const int32_t signal)
167 {
168 for (size_t i = 0; i < sizeof(g_platformSignals) / sizeof(g_platformSignals[0]); i++) {
169 if (signal == g_platformSignals[i].sigNo) {
170 return g_platformSignals[i].name;
171 }
172 }
173 return "Uncare Signal";
174 }
175
ExecLocalDumpUnwinding(int fd,unw_context_t * ctx,size_t skipFrameNum)176 __attribute__((noinline)) void ExecLocalDumpUnwinding(int fd, unw_context_t *ctx, size_t skipFrameNum)
177 {
178 unw_cursor_t cursor;
179 unw_init_local_with_as(g_addrSpace, &cursor, ctx);
180
181 size_t index = 0;
182 unw_word_t pc = 0;
183 unw_word_t prevPc = 0;
184 unw_word_t offset = 0;
185 char symbol[SYMBOL_BUF_SIZE];
186 do {
187 // skip 0 stack, as this is dump catcher. Caller don't need it.
188 if (index < skipFrameNum) {
189 index++;
190 continue;
191 }
192 int ret = unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *)(&pc));
193 BEGET_DFX_CHECK(fd, ret == 0, break, "Failed to get current pc, stop.\n");
194 size_t curIndex = index - skipFrameNum;
195 BEGET_DFX_CHECK(fd, !(curIndex > 1 && prevPc == pc), break, "Invalid pc %l %l, stop.", prevPc, pc);
196 prevPc = pc;
197
198 unw_word_t relPc = unw_get_rel_pc(&cursor);
199 unw_word_t sz = unw_get_previous_instr_sz(&cursor);
200 if ((curIndex > 0) && (relPc > sz)) {
201 relPc -= sz;
202 pc -= sz;
203 #if defined(__arm__)
204 unw_set_adjust_pc(&cursor, pc);
205 #endif
206 }
207
208 struct map_info *map = unw_get_map(&cursor);
209 (void)memset_s(&symbol, sizeof(symbol), 0, sizeof(symbol));
210 if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), (unw_word_t *)(&offset)) == 0) {
211 PrintLog(fd, "#%02d %016p %s(%s+%lu)\n",
212 curIndex, relPc, map == NULL ? "Unknown" : map->path, symbol, offset);
213 } else {
214 PrintLog(fd, "#%02d %016p %s\n", curIndex, relPc, map == NULL ? "Unknown" : map->path);
215 }
216 index++;
217 } while ((unw_step(&cursor) > 0) && (index < BACK_STACK_MAX_STEPS));
218 return;
219 }
220
CrashLocalHandler(const ProcessDumpRequest * request)221 static void CrashLocalHandler(const ProcessDumpRequest *request)
222 {
223 int fd = GetKmsgFd();
224 BEGET_ERROR_CHECK(fd >= 0, return, "Invalid fd");
225
226 PrintLog(fd, "Pid:%d\n", request->pid);
227 PrintLog(fd, "Uid:%d\n", request->uid);
228 PrintLog(fd, "Reason:Signal(%s)\n", GetSignalName(request->siginfo.si_signo));
229
230 unw_context_t context;
231 SetRegister(&context, &(request->context));
232 ExecLocalDumpUnwinding(fd, &context, 0);
233 PrintfRegister(&context, fd);
234 (void)fsync(fd);
235 (void)close(fd);
236 }
237
ReserveChildThreadSignalStack(void)238 static void ReserveChildThreadSignalStack(void)
239 {
240 // reserve stack for fork
241 g_reservedChildStack = mmap(NULL, LOCAL_HANDLER_STACK_SIZE, PROT_READ | PROT_WRITE,
242 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
243 if (g_reservedChildStack == NULL) {
244 BEGET_LOGE("Failed to alloc memory for child stack.");
245 return;
246 }
247 g_reservedChildStack = (void *)(((uint8_t *)g_reservedChildStack) + LOCAL_HANDLER_STACK_SIZE - 1);
248 }
249
DoCrashHandler(void * arg)250 static int DoCrashHandler(void *arg)
251 {
252 BEGET_LOGI("DoCrashHandler");
253 (void)arg;
254 CrashLocalHandler(&g_request);
255 ExecReboot("panic");
256 return 0;
257 }
258
SignalHandler(int sig,siginfo_t * si,void * context)259 static void SignalHandler(int sig, siginfo_t *si, void *context)
260 {
261 (void)memset_s(&g_request, sizeof(g_request), 0, sizeof(g_request));
262 g_request.type = sig;
263 g_request.tid = gettid();
264 g_request.pid = getpid();
265 g_request.timeStamp = GetTimeMilliseconds();
266 BEGET_LOGI("CrashHandler :: sig(%d), pid(%d), tid(%d).", sig, g_request.pid, g_request.tid);
267
268 int ret = memcpy_s(&(g_request.siginfo), sizeof(siginfo_t), si, sizeof(siginfo_t));
269 if (ret < 0) {
270 BEGET_LOGE("memcpy_s siginfo fail, ret=%d", ret);
271 }
272 ret = memcpy_s(&(g_request.context), sizeof(ucontext_t), context, sizeof(ucontext_t));
273 if (ret < 0) {
274 BEGET_LOGE("memcpy_s context fail, ret=%d", ret);
275 }
276
277 int pseudothreadTid = -1;
278 pid_t childTid = clone(DoCrashHandler, g_reservedChildStack,
279 CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
280 &pseudothreadTid, NULL, NULL, &pseudothreadTid);
281 if (childTid == -1) {
282 BEGET_LOGE("Failed to create thread for crash local handler");
283 ExecReboot("panic");
284 } else {
285 sleep(5); // wait 5s
286 ExecReboot("panic");
287 }
288
289 BEGET_LOGI("child thread(%d) exit.", childTid);
290 unw_destroy_local_address_space(g_addrSpace);
291 }
292
InstallLocalSignalHandler(void)293 void InstallLocalSignalHandler(void)
294 {
295 ReserveChildThreadSignalStack();
296 unw_init_local_address_space(&g_addrSpace);
297 sigset_t set;
298 sigemptyset(&set);
299 struct sigaction action;
300 memset_s(&action, sizeof(action), 0, sizeof(action));
301 action.sa_sigaction = SignalHandler;
302 action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
303
304 for (size_t i = 0; i < sizeof(g_platformSignals) / sizeof(g_platformSignals[0]); i++) {
305 int32_t sig = g_platformSignals[i].sigNo;
306 sigemptyset(&action.sa_mask);
307 sigaddset(&action.sa_mask, sig);
308
309 sigaddset(&set, sig);
310 if (sigaction(sig, &action, NULL) != 0) {
311 BEGET_LOGE("Failed to register signal(%d)", sig);
312 }
313 }
314 sigprocmask(SIG_UNBLOCK, &set, NULL);
315 }