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