• 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 
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