1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "dfx_signal_handler.h"
16
17 #ifndef _GNU_SOURCE
18 #define _GNU_SOURCE 1
19 #endif
20
21 #include <fcntl.h>
22 #include <pthread.h>
23 #include <sched.h>
24 #include <signal.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <sys/capability.h>
28 #include <sys/mman.h>
29 #include <sys/prctl.h>
30 #include <sys/syscall.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/uio.h>
34 #include <sys/wait.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include "dfx_define.h"
38 #include "errno.h"
39 #include "linux/capability.h"
40 #include "stdbool.h"
41 #include "string.h"
42 #ifndef DFX_SIGNAL_LIBC
43 #include <securec.h>
44 #include "dfx_cutil.h"
45 #include "dfx_log.h"
46 #else
47 #include "musl_cutil.h"
48 #include "musl_log.h"
49 #endif
50
51 #ifdef LOG_DOMAIN
52 #undef LOG_DOMAIN
53 #define LOG_DOMAIN 0xD002D11
54 #endif
55
56 #ifdef LOG_TAG
57 #undef LOG_TAG
58 #define LOG_TAG "DfxSignalHandler"
59 #endif
60
61 #if defined (__LP64__)
62 #define RESERVED_CHILD_STACK_SIZE (32 * 1024) // 32K
63 #else
64 #define RESERVED_CHILD_STACK_SIZE (16 * 1024) // 16K
65 #endif
66
67 #define BOOL int
68 #define TRUE 1
69 #define FALSE 0
70
71 #ifndef NSIG
72 #define NSIG 64
73 #endif
74
75 #ifndef F_SETPIPE_SZ
76 #define F_SETPIPE_SZ 1031
77 #endif
78
79 #define NUMBER_SIXTYFOUR 64
80 #define INHERITABLE_OFFSET 32
81
InitHandler(void)82 void __attribute__((constructor)) InitHandler(void)
83 {
84 DFX_InstallSignalHandler();
85 }
86
87 static struct ProcessDumpRequest g_request;
88 static struct ProcInfo g_procInfo;
89 static void *g_reservedChildStack;
90 static pthread_mutex_t g_signalHandlerMutex = PTHREAD_MUTEX_INITIALIZER;
91 static int g_pipefd[2] = {-1, -1};
92 static BOOL g_hasInit = FALSE;
93 static const int SIGNALHANDLER_TIMEOUT = 10000; // 10000 us
94 static const int ALARM_TIME_S = 10;
95 static int g_prevHandledSignal = SIGDUMP;
96 static BOOL g_isDumping = FALSE;
97 static struct sigaction g_oldSigactionList[NSIG] = {};
98
99 enum DumpPreparationStage {
100 CREATE_PIPE_FAIL = 1,
101 SET_PIPE_LEN_FAIL,
102 WRITE_PIPE_FAIL,
103 INHERIT_CAP_FAIL,
104 EXEC_FAIL,
105 };
106
107 TraceInfo HiTraceGetId(void) __attribute__((weak));
FillTraceIdLocked(struct ProcessDumpRequest * request)108 static void FillTraceIdLocked(struct ProcessDumpRequest* request)
109 {
110 if (HiTraceGetId == NULL || request == NULL) {
111 return;
112 }
113
114 TraceInfo id = HiTraceGetId();
115 memcpy(&(request->traceInfo), &id, sizeof(TraceInfo));
116 }
117
118 const char* GetLastFatalMessage(void) __attribute__((weak));
FillLastFatalMessageLocked(int32_t sig)119 static void FillLastFatalMessageLocked(int32_t sig)
120 {
121 if (sig != SIGABRT) {
122 return;
123 }
124
125 if (GetLastFatalMessage == NULL) {
126 DfxLogError("Could not find GetLastFatalMessage func");
127 return;
128 }
129
130 const char* lastFatalMessage = GetLastFatalMessage();
131 if (lastFatalMessage == NULL) {
132 DfxLogError("Could not find last message");
133 return;
134 }
135
136 size_t len = strlen(lastFatalMessage);
137 if (len > MAX_FATAL_MSG_SIZE) {
138 DfxLogError("Last message is longer than MAX_FATAL_MSG_SIZE");
139 return;
140 }
141
142 (void)strncpy(g_request.lastFatalMessage, lastFatalMessage, sizeof(g_request.lastFatalMessage) - 1);
143 }
144
InheritCapabilities(void)145 static int32_t InheritCapabilities(void)
146 {
147 struct __user_cap_header_struct capHeader;
148 memset(&capHeader, 0, sizeof(capHeader));
149
150 capHeader.version = _LINUX_CAPABILITY_VERSION_3;
151 capHeader.pid = 0;
152 struct __user_cap_data_struct capData[2];
153 if (capget(&capHeader, &capData[0]) == -1) {
154 DfxLogError("Failed to get origin cap data");
155 return -1;
156 }
157
158 capData[0].inheritable = capData[0].permitted;
159 capData[1].inheritable = capData[1].permitted;
160 if (capset(&capHeader, &capData[0]) == -1) {
161 DfxLogError("Failed to set cap data");
162 return -1;
163 }
164
165 uint64_t ambCap = capData[0].inheritable;
166 ambCap = ambCap | (((uint64_t)capData[1].inheritable) << INHERITABLE_OFFSET);
167 for (size_t i = 0; i < NUMBER_SIXTYFOUR; i++) {
168 if (ambCap & ((uint64_t)1)) {
169 (void)prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0);
170 }
171 ambCap = ambCap >> 1;
172 }
173 return 0;
174 }
175
176 static const int g_interestedSignalList[] = {
177 SIGABRT, SIGBUS, SIGDUMP, SIGFPE, SIGILL,
178 SIGSEGV, SIGSTKFLT, SIGSYS, SIGTRAP,
179 };
180
SetInterestedSignalMasks(int how)181 static void SetInterestedSignalMasks(int how)
182 {
183 sigset_t set;
184 sigemptyset(&set);
185 for (size_t i = 0; i < sizeof(g_interestedSignalList) / sizeof(g_interestedSignalList[0]); i++) {
186 sigaddset(&set, g_interestedSignalList[i]);
187 }
188 sigprocmask(how, &set, NULL);
189 }
190
DFX_SetUpEnvironment()191 static void DFX_SetUpEnvironment()
192 {
193 // avoiding fd exhaust
194 const int closeFdCount = 1024;
195 for (int i = 0; i < closeFdCount; i++) {
196 syscall(SYS_close, i);
197 }
198 // clear stdout and stderr
199 int devNull = OHOS_TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
200 if (devNull < 0) {
201 DfxLogError("Failed to open dev/null.");
202 return;
203 }
204
205 OHOS_TEMP_FAILURE_RETRY(dup2(devNull, STDOUT_FILENO));
206 OHOS_TEMP_FAILURE_RETRY(dup2(devNull, STDERR_FILENO));
207 syscall(SYS_close, devNull);
208 SetInterestedSignalMasks(SIG_BLOCK);
209 }
210
DFX_SetUpSigAlarmAction(void)211 static void DFX_SetUpSigAlarmAction(void)
212 {
213 if (signal(SIGALRM, SIG_DFL) == SIG_ERR) {
214 DfxLogError("signal error!");
215 }
216 sigset_t set;
217 sigemptyset(&set);
218 sigaddset(&set, SIGALRM);
219 sigprocmask(SIG_UNBLOCK, &set, NULL);
220 }
221
DFX_ExecDump(void * arg)222 static int DFX_ExecDump(void *arg)
223 {
224 (void)arg;
225 DFX_SetUpEnvironment();
226 DFX_SetUpSigAlarmAction();
227 alarm(ALARM_TIME_S);
228 // create pipe for passing request to processdump
229 if (pipe(g_pipefd) != 0) {
230 DfxLogError("Failed to create pipe for transfering context.");
231 return CREATE_PIPE_FAIL;
232 }
233 ssize_t writeLen = (long)(sizeof(struct ProcessDumpRequest));
234 if (fcntl(g_pipefd[1], F_SETPIPE_SZ, writeLen) < writeLen) {
235 DfxLogError("Failed to set pipe buffer size.");
236 return SET_PIPE_LEN_FAIL;
237 }
238
239 struct iovec iovs[1] = {
240 {
241 .iov_base = &g_request,
242 .iov_len = sizeof(struct ProcessDumpRequest)
243 },
244 };
245 ssize_t realWriteSize = OHOS_TEMP_FAILURE_RETRY(writev(g_pipefd[1], iovs, 1));
246 if ((ssize_t)writeLen != realWriteSize) {
247 DfxLogError("Failed to write pipe.");
248 return WRITE_PIPE_FAIL;
249 }
250 OHOS_TEMP_FAILURE_RETRY(dup2(g_pipefd[0], STDIN_FILENO));
251 if (g_pipefd[0] != STDIN_FILENO) {
252 syscall(SYS_close, g_pipefd[0]);
253 }
254 syscall(SYS_close, g_pipefd[1]);
255
256 if (InheritCapabilities() != 0) {
257 DfxLogError("Failed to inherit Capabilities from parent.");
258 return INHERIT_CAP_FAIL;
259 }
260 DfxLogInfo("execl processdump.");
261 #ifdef DFX_LOG_USE_HILOG_BASE
262 execl("/system/bin/processdump", "processdump", "-signalhandler", NULL);
263 #else
264 execl("/bin/processdump", "processdump", "-signalhandler", NULL);
265 #endif
266 DfxLogError("Failed to execl processdump, errno: %d(%s)", errno, strerror(errno));
267 return errno;
268 }
269
GetPid(void)270 static pid_t GetPid(void)
271 {
272 return g_procInfo.pid;
273 }
274
GetTid(void)275 static pid_t GetTid(void)
276 {
277 return g_procInfo.tid;
278 }
279
IsMainThread(void)280 static bool IsMainThread(void)
281 {
282 if (g_procInfo.ns) {
283 if (GetTid() == 1) {
284 return true;
285 }
286 } else {
287 if (GetPid() == GetTid()) {
288 return true;
289 }
290 }
291 return false;
292 }
293
ResetSignalHandlerIfNeed(int sig)294 static void ResetSignalHandlerIfNeed(int sig)
295 {
296 if (sig == SIGDUMP) {
297 return;
298 }
299
300 if (g_oldSigactionList[sig].sa_sigaction == NULL) {
301 signal(sig, SIG_DFL);
302 // real init will not handle crash signal
303 // crash in pid namespace may not exit even if rethrow the signal
304 // use exit instead
305 if (GetPid() == 1) {
306 DfxLogError("Sandbox process is about to exit with signal %d.", sig);
307 _exit(sig);
308 }
309 return;
310 }
311
312 if (sigaction(sig, &(g_oldSigactionList[sig]), NULL) != 0) {
313 DfxLogError("Failed to reset signal.");
314 signal(sig, SIG_DFL);
315 }
316 }
317
PauseMainThreadHandler(int sig)318 static void PauseMainThreadHandler(int sig)
319 {
320 // only work when subthread crash and send SIGDUMP to mainthread.
321 pthread_mutex_lock(&g_signalHandlerMutex);
322 pthread_mutex_unlock(&g_signalHandlerMutex);
323 DfxLogInfo("Crash in child thread(%d), exit main thread.", GetTid());
324 }
325
BlockMainThreadIfNeed(int sig)326 static void BlockMainThreadIfNeed(int sig)
327 {
328 if (IsMainThread() || (sig == SIGDUMP)) {
329 return;
330 }
331
332 DfxLogInfo("Crash(%d) in child thread(%d), try stop main thread.", sig, GetTid());
333 (void)signal(SIGDUMP, PauseMainThreadHandler);
334 if (syscall(SYS_tgkill, GetPid(), GetPid(), SIGDUMP) != 0) {
335 DfxLogError("Failed to send SIGDUMP to main thread, errno(%d).", errno);
336 }
337 }
338
ForkBySyscall(void)339 static pid_t ForkBySyscall(void)
340 {
341 #ifdef SYS_fork
342 return syscall(SYS_fork);
343 #else
344 return syscall(SYS_clone, SIGCHLD, 0);
345 #endif
346 }
347
SetDumpState(void)348 static bool SetDumpState(void)
349 {
350 if (prctl(PR_SET_DUMPABLE, 1) != 0) {
351 DfxLogError("Failed to set dumpable.");
352 return false;
353 }
354
355 if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) {
356 if (errno != EINVAL) {
357 DfxLogError("Failed to set ptracer.");
358 return false;
359 }
360 }
361 return true;
362 }
363
RestoreDumpState(int prevState,bool isTracerStatusModified)364 static void RestoreDumpState(int prevState, bool isTracerStatusModified)
365 {
366 prctl(PR_SET_DUMPABLE, prevState);
367 if (isTracerStatusModified == true) {
368 prctl(PR_SET_PTRACER, 0);
369 }
370 }
371
DoProcessDump(void * arg)372 static int DoProcessDump(void* arg)
373 {
374 (void)arg;
375 int status;
376 int ret = -1;
377 int childPid = -1;
378 int startTime = (int)time(NULL);
379 bool isTimeout = false;
380 g_request.recycleTid = syscall(SYS_gettid);
381 // set privilege for dump ourself
382 int prevDumpableStatus = prctl(PR_GET_DUMPABLE);
383 bool isTracerStatusModified = SetDumpState();
384 if (!isTracerStatusModified) {
385 goto out;
386 }
387
388 // fork a child process that could ptrace us
389 childPid = ForkBySyscall();
390 if (childPid == 0) {
391 _exit(DFX_ExecDump(NULL));
392 } else if (childPid < 0) {
393 DfxLogError("Failed to fork child process, errno(%d).", errno);
394 goto out;
395 }
396
397 do {
398 errno = 0;
399 ret = waitpid(childPid, &status, WNOHANG);
400 if (ret < 0) {
401 DfxLogError("Failed to wait child process terminated, errno(%d)", errno);
402 goto out;
403 }
404
405 if (ret == childPid) {
406 break;
407 }
408
409 if ((int)time(NULL) - startTime > PROCESSDUMP_TIMEOUT) {
410 isTimeout = true;
411 break;
412 }
413 usleep(SIGNALHANDLER_TIMEOUT); // sleep 10ms
414 } while (1);
415 DfxLogInfo("(%d) wait for process(%d) return with ret(%d) status(%d) timeout(%d)",
416 g_request.recycleTid, childPid, ret, status, isTimeout);
417 out:
418 g_isDumping = FALSE;
419 RestoreDumpState(prevDumpableStatus, isTracerStatusModified);
420 pthread_mutex_unlock(&g_signalHandlerMutex);
421 return 0;
422 }
423
UnlockSignalHandlerMutex()424 static void UnlockSignalHandlerMutex()
425 {
426 if (pthread_mutex_trylock(&g_signalHandlerMutex) == 0) {
427 pthread_mutex_unlock(&g_signalHandlerMutex);
428 } else {
429 pthread_mutex_unlock(&g_signalHandlerMutex);
430 }
431 }
432
ForkAndDoProcessDump()433 static void ForkAndDoProcessDump()
434 {
435 int prevDumpableStatus = prctl(PR_GET_DUMPABLE);
436 bool isTracerStatusModified = SetDumpState();
437 int childPid = ForkBySyscall();
438 if (childPid == 0) {
439 g_request.vmPid = syscall(SYS_getpid);
440 DfxLogInfo("Start DoProcessDump in VmProcess(%d).", g_request.vmPid);
441 alarm(ALARM_TIME_S);
442 _exit(DoProcessDump(NULL));
443 } else if (childPid < 0) {
444 DfxLogError("Failed to vfork child process, errno(%d).", errno);
445 RestoreDumpState(prevDumpableStatus, isTracerStatusModified);
446 DoProcessDump(NULL);
447 return;
448 }
449
450 DfxLogInfo("Start wait for VmProcess(%d) exit.", childPid);
451 errno = 0;
452 int status;
453 int ret = waitpid(childPid, &status, 0);
454 RestoreDumpState(prevDumpableStatus, isTracerStatusModified);
455 DfxLogInfo("(%d) wait for VmProcess(%d) return with ret(%d) status(%d)",
456 getpid(), childPid, ret, status);
457 }
458
DFX_SignalHandler(int sig,siginfo_t * si,void * context)459 static void DFX_SignalHandler(int sig, siginfo_t *si, void *context)
460 {
461 if (sig == SIGDUMP && g_isDumping) {
462 DfxLogInfo("Current Process is dumping stacktrace now.");
463 return;
464 }
465
466 // crash signal should never be skipped
467 pthread_mutex_lock(&g_signalHandlerMutex);
468 memset(&g_procInfo, 0, sizeof(g_procInfo));
469 GetProcStatus(&g_procInfo);
470 BlockMainThreadIfNeed(sig);
471 if (g_prevHandledSignal != SIGDUMP) {
472 ResetSignalHandlerIfNeed(sig);
473 if (syscall(SYS_rt_tgsigqueueinfo, GetPid(), GetTid(), si->si_signo, si) != 0) {
474 DfxLogError("Failed to resend signal(%d), errno(%d).", si->si_signo, errno);
475 } else {
476 DfxLogInfo("Current process has encount a crash, rethrow sig(%d).", si->si_signo);
477 }
478 return;
479 }
480 g_prevHandledSignal = sig;
481 g_isDumping = TRUE;
482 memset(&g_request, 0, sizeof(g_request));
483 g_request.type = sig;
484 g_request.pid = GetPid();
485 g_request.tid = syscall(SYS_gettid);
486 g_request.uid = getuid();
487 g_request.reserved = 0;
488 g_request.timeStamp = GetTimeMilliseconds();
489 DfxLogInfo("DFX_SignalHandler :: sig(%d), pid(%d), tid(%d).", sig, g_request.pid, g_request.tid);
490
491 GetThreadName(g_request.threadName, sizeof(g_request.threadName));
492 GetProcessName(g_request.processName, sizeof(g_request.processName));
493
494 memcpy(&(g_request.siginfo), si, sizeof(siginfo_t));
495 memcpy(&(g_request.context), context, sizeof(ucontext_t));
496
497 FillTraceIdLocked(&g_request);
498 FillLastFatalMessageLocked(sig);
499
500 // for protecting g_reservedChildStack
501 // g_signalHandlerMutex will be unlocked in DoProcessDump function
502 if (sig != SIGDUMP) {
503 ForkAndDoProcessDump();
504 ResetSignalHandlerIfNeed(sig);
505 if (syscall(SYS_rt_tgsigqueueinfo, syscall(SYS_getpid), syscall(SYS_gettid), si->si_signo, si) != 0) {
506 DfxLogError("Failed to resend signal(%d), errno(%d).", si->si_signo, errno);
507 }
508 UnlockSignalHandlerMutex();
509 } else {
510 int recycleTid = clone(DoProcessDump, g_reservedChildStack, CLONE_THREAD | CLONE_SIGHAND | CLONE_VM, NULL);
511 if (recycleTid == -1) {
512 DfxLogError("Failed to create thread for recycle dump process");
513 pthread_mutex_unlock(&g_signalHandlerMutex);
514 }
515 }
516
517 DfxLogInfo("Finish handle signal(%d) in %d:%d", sig, g_request.pid, g_request.tid);
518 }
519
DFX_InstallSignalHandler(void)520 void DFX_InstallSignalHandler(void)
521 {
522 if (g_hasInit) {
523 return;
524 }
525
526 // reserve stack for fork
527 g_reservedChildStack = mmap(NULL, RESERVED_CHILD_STACK_SIZE, \
528 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 1, 0);
529 if (g_reservedChildStack == NULL) {
530 DfxLogError("Failed to alloc memory for child stack.");
531 return;
532 }
533 g_reservedChildStack = (void *)(((uint8_t *)g_reservedChildStack) + RESERVED_CHILD_STACK_SIZE - 1);
534
535 struct sigaction action;
536 memset(&action, 0, sizeof(action));
537 memset(&g_oldSigactionList, 0, sizeof(g_oldSigactionList));
538 sigfillset(&action.sa_mask);
539 action.sa_sigaction = DFX_SignalHandler;
540 action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
541
542 for (size_t i = 0; i < sizeof(g_interestedSignalList) / sizeof(g_interestedSignalList[0]); i++) {
543 int32_t sig = g_interestedSignalList[i];
544 if (sigaction(sig, &action, &(g_oldSigactionList[sig])) != 0) {
545 DfxLogError("Failed to register signal(%d)", sig);
546 }
547 }
548
549 g_hasInit = TRUE;
550 }
551