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