• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 #ifndef _GNU_SOURCE
16 #define _GNU_SOURCE 1
17 #endif
18 
19 #include "dfx_dumprequest.h"
20 
21 #include <fcntl.h>
22 #include <poll.h>
23 #include <pthread.h>
24 #include <sched.h>
25 #include <signal.h>
26 #include <sigchain.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <sys/capability.h>
30 #include <sys/mman.h>
31 #include <sys/prctl.h>
32 #include <sys/syscall.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/uio.h>
36 #include <sys/wait.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include "dfx_define.h"
40 #include "dfx_dump_request.h"
41 #include "dfx_signalhandler_exception.h"
42 #include "errno.h"
43 #include "linux/capability.h"
44 #include "stdbool.h"
45 #include "string.h"
46 #ifndef DFX_SIGNAL_LIBC
47 #include <securec.h>
48 #include "dfx_cutil.h"
49 #include "dfx_log.h"
50 #else
51 #include "musl_cutil.h"
52 #include "musl_log.h"
53 #endif
54 
55 #include "info/fatal_message.h"
56 
57 #ifdef LOG_DOMAIN
58 #undef LOG_DOMAIN
59 #define LOG_DOMAIN 0xD002D11
60 #endif
61 
62 #ifdef LOG_TAG
63 #undef LOG_TAG
64 #define LOG_TAG "DfxSignalHandler"
65 #endif
66 
67 #ifndef F_SETPIPE_SZ
68 #define F_SETPIPE_SZ 1031
69 #endif
70 
71 #define NUMBER_SIXTYFOUR 64
72 #define INHERITABLE_OFFSET 32
73 
74 static struct ProcessDumpRequest *g_request = NULL;
75 
76 static long g_blockExit = 0;
77 static long g_vmRealPid = 0;
78 static long g_unwindResult = 0;
79 
80 enum PIPE_FD_TYPE {
81     WRITE_TO_DUMP,
82     READ_FROM_DUMP_TO_CHILD,
83     PIPE_MAX,
84 };
85 
86 static int g_pipeFds[PIPE_MAX][2] = {
87     {-1, -1},
88     {-1, -1}
89 };
90 
91 static const int ALARM_TIME_S = 10;
92 static const uint32_t CRASH_SNAPSHOT_FLAG = 0x8;
93 enum DumpPreparationStage {
94     CREATE_PIPE_FAIL = 1,
95     SET_PIPE_LEN_FAIL,
96     WRITE_PIPE_FAIL,
97     INHERIT_CAP_FAIL,
98     EXEC_FAIL,
99 };
100 
101 static void CleanFd(int *pipeFd);
102 static void CleanPipe(void);
103 static bool InitPipe(void);
104 static bool ReadPipeTimeout(int fd, uint64_t timeout, uint32_t* value);
105 static bool ReadProcessDumpGetRegsMsg(void);
106 
ResetFlags(void)107 static void ResetFlags(void)
108 {
109     g_unwindResult = 0;
110     g_blockExit = 0;
111 }
112 
IsDumpSignal(int signo)113 static bool IsDumpSignal(int signo)
114 {
115     return signo == SIGDUMP || signo == SIGLEAK_STACK;
116 }
117 
GetCrashDescription(const int32_t errCode)118 static const char* GetCrashDescription(const int32_t errCode)
119 {
120     size_t i;
121 
122     for (i = 0; i < sizeof(g_crashExceptionMap) / sizeof(g_crashExceptionMap[0]); i++) {
123         if (errCode == g_crashExceptionMap[i].errCode) {
124             return g_crashExceptionMap[i].str;
125         }
126     }
127     return g_crashExceptionMap[i - 1].str;    /* the end of map is "unknown reason" */
128 }
129 
FillCrashExceptionAndReport(const int err)130 static void FillCrashExceptionAndReport(const int err)
131 {
132     struct CrashDumpException exception;
133     (void)memset_s(&exception, sizeof(struct CrashDumpException), 0, sizeof(struct CrashDumpException));
134     exception.pid = g_request->pid;
135     exception.uid = (int32_t)(g_request->uid);
136     exception.error = err;
137     exception.time = (int64_t)(GetTimeMilliseconds());
138     if (strncpy_s(exception.message, sizeof(exception.message), GetCrashDescription(err),
139         sizeof(exception.message) - 1) != 0) {
140         DFXLOGE("strcpy exception message fail");
141         return;
142     }
143     ReportException(&exception);
144 }
145 
InheritCapabilities(void)146 static int32_t InheritCapabilities(void)
147 {
148     struct __user_cap_header_struct capHeader;
149     (void)memset_s(&capHeader, sizeof(capHeader), 0, sizeof(capHeader));
150 
151     capHeader.version = _LINUX_CAPABILITY_VERSION_3;
152     capHeader.pid = 0;
153     struct __user_cap_data_struct capData[2];
154     if (capget(&capHeader, &capData[0]) == -1) {
155         DFXLOGE("Failed to get origin cap data");
156         return -1;
157     }
158 
159     capData[0].inheritable = capData[0].permitted;
160     capData[1].inheritable = capData[1].permitted;
161     if (capset(&capHeader, &capData[0]) == -1) {
162         DFXLOGE("Failed to set cap data, errno(%{public}d)", errno);
163         return -1;
164     }
165 
166     uint64_t ambCap = capData[0].inheritable;
167     ambCap = ambCap | (((uint64_t)capData[1].inheritable) << INHERITABLE_OFFSET);
168     for (size_t i = 0; i < NUMBER_SIXTYFOUR; i++) {
169         if (ambCap & ((uint64_t)1)) {
170             if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) {
171                 DFXLOGE("Failed to change the ambient capability set, errno(%{public}d)", errno);
172             }
173         }
174         ambCap = ambCap >> 1;
175     }
176     return 0;
177 }
178 
179 static const int SIGCHAIN_DUMP_SIGNAL_LIST[] = {
180     SIGDUMP, SIGLEAK_STACK
181 };
182 
183 static const int SIGCHAIN_CRASH_SIGNAL_LIST[] = {
184     SIGILL, SIGABRT, SIGBUS, SIGFPE,
185     SIGSEGV, SIGSTKFLT, SIGSYS, SIGTRAP
186 };
187 
SetInterestedSignalMasks(int how)188 static void SetInterestedSignalMasks(int how)
189 {
190     sigset_t set;
191     sigemptyset(&set);
192     for (size_t i = 0; i < sizeof(SIGCHAIN_DUMP_SIGNAL_LIST) / sizeof(SIGCHAIN_DUMP_SIGNAL_LIST[0]); i++) {
193         sigaddset(&set, SIGCHAIN_DUMP_SIGNAL_LIST[i]);
194     }
195     for (size_t i = 0; i < sizeof(SIGCHAIN_CRASH_SIGNAL_LIST) / sizeof(SIGCHAIN_CRASH_SIGNAL_LIST[0]); i++) {
196         sigaddset(&set, SIGCHAIN_CRASH_SIGNAL_LIST[i]);
197     }
198     sigprocmask(how, &set, NULL);
199 }
200 
CloseFds(void)201 static void CloseFds(void)
202 {
203     const int startIndex = 128;  // 128 : avoid set pipe fail
204     const int closeFdCount = 1024;
205     for (int i = startIndex; i < closeFdCount; i++) {
206         syscall(SYS_close, i);
207     }
208 }
209 
DFX_SetUpEnvironment(void)210 static void DFX_SetUpEnvironment(void)
211 {
212     // clear stdout and stderr
213     int devNull = OHOS_TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
214     if (devNull < 0) {
215         DFXLOGE("Failed to open dev/null.");
216         return;
217     }
218 
219     OHOS_TEMP_FAILURE_RETRY(dup2(devNull, STDOUT_FILENO));
220     OHOS_TEMP_FAILURE_RETRY(dup2(devNull, STDERR_FILENO));
221     syscall(SYS_close, devNull);
222     SetInterestedSignalMasks(SIG_BLOCK);
223 }
224 
DFX_SetUpSigAlarmAction(void)225 static void DFX_SetUpSigAlarmAction(void)
226 {
227     if (signal(SIGALRM, SIG_DFL) == SIG_ERR) {
228         DFXLOGW("Default signal alarm error!");
229     }
230     sigset_t set;
231     sigemptyset(&set);
232     sigaddset(&set, SIGALRM);
233     sigprocmask(SIG_UNBLOCK, &set, NULL);
234 }
235 
DFX_ExecDump(void)236 static int DFX_ExecDump(void)
237 {
238     DFX_SetUpEnvironment();
239     DFX_SetUpSigAlarmAction();
240     alarm(ALARM_TIME_S);
241     int pipefd[2] = {-1, -1};
242     // create pipe for passing request to processdump
243     pipefd[0] = g_pipeFds[WRITE_TO_DUMP][0];
244     pipefd[1] = g_pipeFds[WRITE_TO_DUMP][1];
245 
246     ssize_t writeLen = (long)(sizeof(struct ProcessDumpRequest));
247     if (fcntl(pipefd[1], F_SETPIPE_SZ, writeLen) < writeLen) {
248         DFXLOGE("Failed to set pipe buffer size, errno(%{public}d).", errno);
249         return SET_PIPE_LEN_FAIL;
250     }
251 
252     struct iovec iovs[1] = {
253         {
254             .iov_base = g_request,
255             .iov_len = sizeof(struct ProcessDumpRequest)
256         },
257     };
258     if (OHOS_TEMP_FAILURE_RETRY(writev(pipefd[1], iovs, 1)) != writeLen) {
259         DFXLOGE("Failed to write pipe, errno(%{public}d)", errno);
260         return WRITE_PIPE_FAIL;
261     }
262     OHOS_TEMP_FAILURE_RETRY(dup2(pipefd[0], STDIN_FILENO));
263     if (pipefd[0] != STDIN_FILENO) {
264         syscall(SYS_close, pipefd[0]);
265     }
266     syscall(SYS_close, pipefd[1]);
267 
268     if (InheritCapabilities() != 0) {
269         DFXLOGE("Failed to inherit Capabilities from parent.");
270         FillCrashExceptionAndReport(CRASH_SIGNAL_EINHERITCAP);
271         return INHERIT_CAP_FAIL;
272     }
273     DFXLOGI("execl processdump.");
274 #ifdef DFX_LOG_HILOG_BASE
275     execl("/system/bin/processdump", "processdump", "-signalhandler", NULL);
276 #else
277     execl("/bin/processdump", "processdump", "-signalhandler", NULL);
278 #endif
279     DFXLOGE("Failed to execl processdump, errno(%{public}d)", errno);
280     FillCrashExceptionAndReport(CRASH_SIGNAL_EEXECL);
281     return errno;
282 }
283 
ForkBySyscall(void)284 static pid_t ForkBySyscall(void)
285 {
286 #ifdef SYS_fork
287     return syscall(SYS_fork);
288 #else
289     return syscall(SYS_clone, SIGCHLD, 0);
290 #endif
291 }
292 
SetDumpState(void)293 static bool SetDumpState(void)
294 {
295     if (prctl(PR_SET_DUMPABLE, 1) != 0) {
296         DFXLOGE("Failed to set dumpable, errno(%{public}d).", errno);
297         return false;
298     }
299 
300     if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) {
301         if (errno != EINVAL) {
302             DFXLOGE("Failed to set ptracer, errno(%{public}d).", errno);
303             return false;
304         }
305     }
306     return true;
307 }
308 
RestoreDumpState(int prevState,bool isTracerStatusModified)309 static void RestoreDumpState(int prevState, bool isTracerStatusModified)
310 {
311     prctl(PR_SET_DUMPABLE, prevState);
312     if (isTracerStatusModified == true) {
313         prctl(PR_SET_PTRACER, 0);
314     }
315 }
316 
SafeDelayOneMillSec(void)317 static void SafeDelayOneMillSec(void)
318 {
319     struct timespec ts;
320     ts.tv_sec = 0;
321     ts.tv_nsec = 1000000; // 1000000 : 1ms
322     OHOS_TEMP_FAILURE_RETRY(nanosleep(&ts, &ts));
323 }
324 
IsWaitpidTimeout(pid_t pid,int timeout)325 static bool IsWaitpidTimeout(pid_t pid, int timeout)
326 {
327     while (timeout > 0) {
328         int res = waitpid(pid, NULL, WNOHANG);
329         if (res > 0) {
330             break;
331         } else if (res < 0) {
332             DFXLOGE("failed to wait dummy processdump(%{public}d)", errno);
333             break;
334         }
335         SafeDelayOneMillSec();
336         timeout--;
337         if (timeout == 0) {
338             return true;
339         }
340     }
341     return false;
342 }
343 
StartProcessdump(void)344 static bool StartProcessdump(void)
345 {
346     uint64_t startTime = GetAbsTimeMilliSeconds();
347     pid_t pid = ForkBySyscall();
348     if (pid < 0) {
349         DFXLOGE("Failed to fork dummy processdump(%{public}d)", errno);
350         return false;
351     } else if (pid == 0) {
352         if (!InitPipe()) {
353             DFXLOGE("init pipe fail");
354             _exit(0);
355         }
356         pid_t processDumpPid = ForkBySyscall();
357         if (processDumpPid < 0) {
358             DFXLOGE("Failed to fork processdump(%{public}d)", errno);
359             _exit(0);
360         } else if (processDumpPid > 0) {
361             ReadProcessDumpGetRegsMsg();
362             _exit(0);
363         } else {
364             uint64_t endTime;
365             int tid;
366             ParseSiValue(&g_request->siginfo, &endTime, &tid);
367             uint64_t curTime = GetAbsTimeMilliSeconds();
368             DFXLOGI("start processdump, fork spend time %{public}" PRIu64 "ms", curTime - startTime);
369             if (endTime != 0) {
370                 DFXLOGI("dump remain %{public}" PRId64 "ms", endTime - curTime);
371             }
372             if (endTime == 0 || endTime > curTime) {
373                 g_request->blockCrashExitAddr = (intptr_t)&g_blockExit;
374                 g_request->vmProcRealPidAddr = (intptr_t)&g_vmRealPid;
375                 g_request->unwindResultAddr = (intptr_t)&g_unwindResult;
376                 DFX_ExecDump();
377             } else {
378                 DFXLOGI("current has spend all time, not execl processdump");
379             }
380             _exit(0);
381         }
382     }
383 
384     int timeOutCnt = 3000; // 3000 : 3 sec timeout
385     if (IsWaitpidTimeout(pid, timeOutCnt)) {
386         DFXLOGI("waitpid %{public}d timeout", pid);
387         kill(pid, SIGKILL);
388         FillCrashExceptionAndReport(CRASH_SIGNAL_EWAITPIDTIMEOUT);
389         return false;
390     }
391     return true;
392 }
393 
StartVMProcessUnwind(void)394 static bool StartVMProcessUnwind(void)
395 {
396     uint32_t startTime = GetAbsTimeMilliSeconds();
397     pid_t pid = ForkBySyscall();
398     if (pid < 0) {
399         DFXLOGE("Failed to fork vm process(%{public}d)", errno);
400         return false;
401     }
402     if (pid == 0) {
403         pid_t vmPid = ForkBySyscall();
404         if (vmPid == 0) {
405             DFXLOGI("start vm process, fork spend time %{public}" PRIu64 "ms", GetAbsTimeMilliSeconds() - startTime);
406             g_vmRealPid = GetRealPid();
407             DFXLOGI("vm prorcecc read pid = %{public}ld", g_vmRealPid);
408             _exit(0);
409         } else {
410             DFXLOGI("exit dummy vm process");
411             _exit(0);
412         }
413     }
414 
415     if (waitpid(pid, NULL, 0) <= 0) {
416         DFXLOGE("failed to wait dummy vm process(%{public}d)", errno);
417     }
418     return true;
419 }
420 
CleanFd(int * pipeFd)421 static void CleanFd(int *pipeFd)
422 {
423     if (*pipeFd != -1) {
424         syscall(SYS_close, *pipeFd);
425         *pipeFd = -1;
426     }
427 }
428 
CleanPipe(void)429 static void CleanPipe(void)
430 {
431     for (size_t i = 0; i < PIPE_MAX; i++) {
432         CleanFd(&g_pipeFds[i][0]);
433         CleanFd(&g_pipeFds[i][1]);
434     }
435 }
436 
InitPipe(void)437 static bool InitPipe(void)
438 {
439     bool ret = true;
440     for (int i = 0; i < PIPE_MAX; i++) {
441         if (syscall(SYS_pipe2, g_pipeFds[i], 0) == -1) {
442             DFXLOGE("create pipe fail, errno(%{public}d)", errno);
443             ret = false;
444             CleanPipe();
445             break;
446         }
447     }
448     if (!ret) {
449         CloseFds();
450         for (int i = 0; i < PIPE_MAX; i++) {
451             if (syscall(SYS_pipe2, g_pipeFds[i], 0) == -1) {
452                 DFXLOGE("create pipe fail again, errno(%{public}d)", errno);
453                 FillCrashExceptionAndReport(CRASH_SIGNAL_ECREATEPIPE);
454                 CleanPipe();
455                 return false;
456             }
457         }
458     }
459 
460     g_request->childPipeFd[0] = g_pipeFds[READ_FROM_DUMP_TO_CHILD][0];
461     g_request->childPipeFd[1] = g_pipeFds[READ_FROM_DUMP_TO_CHILD][1];
462     return true;
463 }
464 
ReadPipeTimeout(int fd,uint64_t timeout,uint32_t * value)465 static bool ReadPipeTimeout(int fd, uint64_t timeout, uint32_t* value)
466 {
467     if (fd < 0 || value == NULL) {
468         return false;
469     }
470     struct pollfd pfds[1];
471     pfds[0].fd = fd;
472     pfds[0].events = POLLIN;
473 
474     uint64_t startTime = GetTimeMilliseconds();
475     uint64_t endTime = startTime + timeout;
476     int pollRet = -1;
477     do {
478         pollRet = poll(pfds, 1, timeout);
479         if ((pollRet > 0) && (pfds[0].revents && POLLIN)) {
480             if (OHOS_TEMP_FAILURE_RETRY(read(fd, value, sizeof(uint32_t))) ==
481                 (long int)(sizeof(uint32_t))) {
482                 return true;
483             }
484         }
485 
486         uint64_t now = GetTimeMilliseconds();
487         if (now >= endTime || now < startTime) {
488             break;
489         } else {
490             timeout = endTime - now;
491         }
492     } while (pollRet < 0 && errno == EINTR);
493     FillCrashExceptionAndReport(CRASH_SIGNAL_EREADPIPE);
494     DFXLOGE("read pipe failed , errno(%{public}d)", errno);
495     return false;
496 }
497 
ReadProcessDumpGetRegsMsg(void)498 static bool ReadProcessDumpGetRegsMsg(void)
499 {
500     CleanFd(&g_pipeFds[READ_FROM_DUMP_TO_CHILD][1]);
501 
502     DFXLOGI("start wait processdump read registers");
503     const uint64_t readRegsTimeout = 5000; // 5s
504     uint32_t isFinishGetRegs = OPE_FAIL;
505     if (ReadPipeTimeout(g_pipeFds[READ_FROM_DUMP_TO_CHILD][0], readRegsTimeout, &isFinishGetRegs)) {
506         if (isFinishGetRegs == OPE_SUCCESS) {
507             DFXLOGI("processdump have get all registers .");
508             return true;
509         }
510     }
511 
512     return false;
513 }
514 
SetKernelSnapshot(bool enable)515 static void SetKernelSnapshot(bool enable)
516 {
517     const char *filePath = "/proc/self/unexpected_die_catch";
518     if (access(filePath, F_OK) < 0) {
519         return;
520     }
521     int dieCatchFd = open(filePath, O_RDWR);
522     if (dieCatchFd < 0) {
523         DFXLOGE("Failed to open unexpecterd_die_catch %{public}d", errno);
524         return;
525     }
526     do {
527         char val[10] = {0}; // 10 : to save diecatch val
528         if (read(dieCatchFd, val, sizeof(val)) < 0) {
529             DFXLOGE("Failed to read unexpecterd_die_catch %{public}d", errno);
530             break;
531         }
532         if (lseek(dieCatchFd, 0, SEEK_SET) < 0) {
533             DFXLOGE("Failed to lseek unexpecterd_die_catch %{public}d", errno);
534             break;
535         }
536 
537         uint32_t num = (uint32_t)strtoul(val, NULL, 16); // 16 : val is hex
538         if (errno == ERANGE) {
539             DFXLOGE("Failed to cast unexpecterd_die_catch val to int %{public}d", errno);
540             break;
541         }
542         if (enable) {
543             num |= CRASH_SNAPSHOT_FLAG;
544         } else {
545             num &= (~CRASH_SNAPSHOT_FLAG);
546         }
547 
548         (void)memset_s(val, sizeof(val), 0, sizeof(val));
549         if (snprintf_s(val, sizeof(val), sizeof(val) - 1, "%x", num) < 0) {
550             DFXLOGE("Failed to format unexpecterd_die_catch val %{public}d", errno);
551             break;
552         }
553         if (write(dieCatchFd, val, sizeof(val)) < 0) {
554             DFXLOGE("Failed to write unexpecterd_die_catch %{public}d", errno);
555         }
556     } while (false);
557     syscall(SYS_close, dieCatchFd);
558 }
559 
ReadUnwindFinishMsg(int signo)560 static void ReadUnwindFinishMsg(int signo)
561 {
562     if (IsDumpSignal(signo)) {
563         return;
564     }
565 
566     DFXLOGI("crash processdump unwind finish, unwind success Flag %{public}ld, blockFlag %{public}ld",
567         g_unwindResult, g_blockExit);
568     if (g_unwindResult == CRASH_UNWIND_SUCCESS_FLAG) {
569         SetKernelSnapshot(false);
570     }
571     if (g_blockExit == CRASH_BLOCK_EXIT_FLAG) {
572         syscall(SYS_tgkill, g_request->nsPid, g_request->tid, SIGSTOP);
573     }
574 }
575 
ProcessDump(int signo)576 static int ProcessDump(int signo)
577 {
578     int prevDumpableStatus = prctl(PR_GET_DUMPABLE);
579     bool isTracerStatusModified = SetDumpState();
580     if (!IsDumpSignal(signo)) {
581         ResetFlags();
582         SetKernelSnapshot(true);
583     }
584 
585     do {
586         uint64_t endTime;
587         int tid;
588         ParseSiValue(&g_request->siginfo, &endTime, &tid);
589         if (endTime != 0 && endTime <= GetAbsTimeMilliSeconds()) {
590             DFXLOGI("enter processdump has coat all time, just exit");
591             break;
592         }
593         if (!StartProcessdump()) {
594             DFXLOGE("start processdump fail");
595             break;
596         }
597 
598         if (!StartVMProcessUnwind()) {
599             DFXLOGE("start vm process unwind fail");
600             break;
601         }
602         ReadUnwindFinishMsg(signo);
603     } while (false);
604 
605     RestoreDumpState(prevDumpableStatus, isTracerStatusModified);
606     return 0;
607 }
608 
DfxDumpRequest(int signo,struct ProcessDumpRequest * request)609 void DfxDumpRequest(int signo, struct ProcessDumpRequest *request)
610 {
611     if (request == NULL) {
612         DFXLOGE("Failed to DumpRequest because of error parameters!");
613         return;
614     }
615     g_request = request;
616     ProcessDump(signo);
617 }
618