• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 #if defined(__aarch64__)
16 #include "dfx_coredump_service.h"
17 
18 #include <fstream>
19 #include <fcntl.h>
20 #include "dfx_define.h"
21 #include "dfx_log.h"
22 #include "dump_utils.h"
23 #include "faultloggerd_client.h"
24 #include "securec.h"
25 #ifndef is_ohos_lite
26 #include "parameters.h"
27 #endif
28 
29 namespace OHOS {
30 namespace HiviewDFX {
31 namespace {
32 const char *const COREDUMP_DIR_PATH = "/data/storage/el2/base/files";
33 const char *const COREDUMP_HAP_WHITE_LIST = "const.dfx.coredump.hap_list";
34 const char *const HWASAN_COREDUMP_ENABLE = "faultloggerd.priv.hwasan_coredump.enabled";
35 char g_coredumpFilePath[256] = {0};
36 static const int ARG2 = 2;
37 static const int ARG16 = 16;
38 static const int ARG100 = 100;
39 static const int ARG1000 = 1000;
40 
41 using namespace OHOS;
42 
GetCoredumpWhiteList()43 static std::string GetCoredumpWhiteList()
44 {
45 #ifndef is_ohos_lite
46     std::string whiteList = OHOS::system::GetParameter(COREDUMP_HAP_WHITE_LIST, "");
47     return whiteList;
48 #endif
49     return "";
50 }
51 
HandleSigterm(int sig)52 void HandleSigterm(int sig)
53 {
54     unlink(g_coredumpFilePath);
55     _exit(0);
56 }
57 
UnBlockSIGTERM()58 int UnBlockSIGTERM()
59 {
60     sigset_t set;
61     sigemptyset(&set);
62     sigaddset(&set, DUMPCATCHER_TIMEOUT);
63     sigprocmask(SIG_UNBLOCK, &set, nullptr);
64     return 0;
65 }
66 
RegisterCancelCoredump(std::string logPath)67 int RegisterCancelCoredump(std::string logPath)
68 {
69     auto ret = strncpy_s(g_coredumpFilePath, sizeof(g_coredumpFilePath), logPath.c_str(), logPath.length());
70     if (ret != 0) {
71         DFXLOGE("strncpy_s fail, err:%{public}d", ret);
72         return -1;
73     }
74 
75     UnBlockSIGTERM();
76     if (signal(DUMPCATCHER_TIMEOUT, HandleSigterm) == SIG_ERR) {
77         DFXLOGE("Failed to register handler for DUMPCATCHER_TIMEOUT");
78         return -1;
79     }
80     return 0;
81 }
82 }
83 
IsHwasanCoredumpEnabled()84 bool CoreDumpService::IsHwasanCoredumpEnabled()
85 {
86 #ifndef is_ohos_lite
87     static bool isHwasanCoredumpEnabled = OHOS::system::GetParameter(HWASAN_COREDUMP_ENABLE, "false") == "true";
88     return isHwasanCoredumpEnabled;
89 #else
90     return false;
91 #endif
92 }
93 
IsCoredumpSignal(const ProcessDumpRequest & request)94 bool CoreDumpService::IsCoredumpSignal(const ProcessDumpRequest& request)
95 {
96     return request.siginfo.si_signo == 42 && request.siginfo.si_code == 3; // 42 3
97 }
98 
CoreDumpService(int32_t targetPid,int32_t targetTid,std::shared_ptr<DfxRegs> keyRegs)99 CoreDumpService::CoreDumpService(int32_t targetPid, int32_t targetTid, std::shared_ptr<DfxRegs> keyRegs)
100 {
101     SetCoreDumpServiceData(targetPid, targetTid, keyRegs);
102 }
103 
SetCoreDumpServiceData(int32_t targetPid,int32_t targetTid,std::shared_ptr<DfxRegs> keyRegs)104 void CoreDumpService::SetCoreDumpServiceData(int32_t targetPid, int32_t targetTid, std::shared_ptr<DfxRegs> keyRegs)
105 {
106     coreDumpThread_.targetPid = targetPid;
107     coreDumpThread_.targetTid = targetTid;
108     keyRegs_ = keyRegs;
109 }
110 
~CoreDumpService()111 CoreDumpService::~CoreDumpService()
112 {
113     DeInit();
114 }
115 
SetVmPid(int32_t vmPid)116 void CoreDumpService::SetVmPid(int32_t vmPid)
117 {
118     coreDumpThread_.vmPid = vmPid;
119 }
120 
StartFirstStageDump(const ProcessDumpRequest & request)121 void CoreDumpService::StartFirstStageDump(const ProcessDumpRequest& request)
122 {
123     SetCoreDumpServiceData(request.pid, request.tid, DfxRegs::CreateFromUcontext(request.context));
124     StartCoreDump();
125     WriteSegmentHeader();
126     WriteNoteSegment();
127 }
128 
StartSecondStageDump(int32_t vmPid,const ProcessDumpRequest & request)129 void CoreDumpService::StartSecondStageDump(int32_t vmPid, const ProcessDumpRequest& request)
130 {
131     if (!IsDoCoredump()) {
132         return;
133     }
134     coreDumpThread_.vmPid = vmPid;
135     int pid = coreDumpThread_.targetPid;
136     WriteLoadSegment();
137     WriteSectionHeader();
138     auto ret = FinishCoreDump();
139     if (!ret) {
140         UnlinkFile(GetCoredumpFilePath());
141     }
142     std::string bundleName = ret ? GetCoredumpFileName() : "";
143     int32_t retCode = ret ? ResponseCode::REQUEST_SUCCESS : ResponseCode::CORE_DUMP_GENERATE_FAIL;
144     FinishCoredumpCb(pid, bundleName, retCode);
145     if (IsHwasanCoredumpEnabled()) {
146         DumpUtils::InfoCrashUnwindResult(request, true);
147     }
148     exit(0);
149 }
150 
GetBundleNameItem()151 std::string CoreDumpService::GetBundleNameItem()
152 {
153     return bundleName_;
154 }
155 
GetCoreDumpThread()156 CoreDumpThread CoreDumpService::GetCoreDumpThread()
157 {
158     return coreDumpThread_;
159 }
160 
StartCoreDump()161 bool CoreDumpService::StartCoreDump()
162 {
163     DFXLOGI("Begin to start coredump");
164     if (status_ != WriteStatus::START_STAGE) {
165         DFXLOGE("The status is not START_STAGE!");
166         return false;
167     }
168     if (!CreateFile()) {
169         return false;
170     }
171     if (!MmapForFd()) {
172         return false;
173     }
174     status_ = WriteStatus::WRITE_SEGMENT_HEADER_STAGE;
175     return true;
176 }
177 
FinishCoreDump()178 bool CoreDumpService::FinishCoreDump()
179 {
180     DFXLOGI("Coredump end: pid = %{public}d, elapsed time = %{public}" PRId64 "ms",
181         coreDumpThread_.targetPid, counter_.Elapsed<std::chrono::milliseconds>());
182     if (status_ != WriteStatus::STOP_STAGE) {
183         DFXLOGE("The status is not STOP_STAGE!");
184         return false;
185     }
186     realCoreFileSize_ = static_cast<uint64_t>(currentPointer_ - mappedMemory_);
187     if (!AdjustFileSize(fd_, realCoreFileSize_)) {
188         return false;
189     }
190     status_ = WriteStatus::DONE_STAGE;
191     return true;
192 }
193 
DeInit()194 void CoreDumpService::DeInit()
195 {
196     if (fd_ != -1) {
197         close(fd_);
198         fd_ = -1;
199     }
200     if (mappedMemory_ != nullptr) {
201         munmap(static_cast<void*>(mappedMemory_), coreFileSize_);
202         mappedMemory_ = nullptr;
203     }
204 }
205 
CreateFile()206 bool CoreDumpService::CreateFile()
207 {
208     if (coreDumpThread_.targetPid == 0) {
209         DFXLOGE("The targetPid is 0!");
210         return false;
211     }
212     coreFileSize_ = GetCoreFileSize(coreDumpThread_.targetPid);
213     if (coreFileSize_ == 0) {
214         DFXLOGE("corefile size is 0");
215         return false;
216     }
217     fd_ = CreateFileForCoreDump();
218     if (fd_ == INVALID_FD) {
219         DFXLOGE("create file fail");
220         return false;
221     }
222     return true;
223 }
224 
MmapForFd()225 bool CoreDumpService::MmapForFd()
226 {
227     if (fd_ == -1) {
228         DFXLOGE("The fd is invalid, not to mmap");
229         return false;
230     }
231     if (coreFileSize_ == 0) {
232         DFXLOGE("The coreFileSize is 0, not to mmap");
233         return false;
234     }
235     if (!AdjustFileSize(fd_, coreFileSize_)) {
236         return false;
237     }
238     mappedMemory_ = static_cast<char *>(mmap(nullptr, coreFileSize_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0));
239     if (mappedMemory_ == MAP_FAILED) {
240         DFXLOGE("mmap fail");
241         mappedMemory_ = nullptr;
242         return false;
243     }
244     DFXLOGI("mmap success");
245     return true;
246 }
247 
WriteSegmentHeader()248 bool CoreDumpService::WriteSegmentHeader()
249 {
250     DFXLOGI("Begin to Write segment header");
251     if (status_ != WriteStatus::WRITE_SEGMENT_HEADER_STAGE) {
252         DFXLOGE("The status is not WRITE_SEGMENT_HEADER_STAGE!");
253         return false;
254     }
255     currentPointer_ = mappedMemory_;
256     currentPointer_ += sizeof(Elf64_Ehdr);
257     ProgramSegmentHeaderWriter programSegmentHeader(mappedMemory_, currentPointer_, ePhnum_, maps_);
258     currentPointer_ = programSegmentHeader.Write();
259     Elf64_Ehdr eh;
260     ElfHeaderFill(eh, ePhnum_);
261     if (memcpy_s(mappedMemory_, sizeof(eh), reinterpret_cast<char*>(&eh), sizeof(eh)) != EOK) {
262         DFXLOGE("memcpy fail");
263         return false;
264     }
265     status_ = WriteStatus::WRITE_NOTE_SEGMENT_STAGE;
266     return true;
267 }
268 
WriteNoteSegment()269 bool CoreDumpService::WriteNoteSegment()
270 {
271     DFXLOGI("Begin to Write note segment");
272     if (status_ != WriteStatus::WRITE_NOTE_SEGMENT_STAGE) {
273         DFXLOGE("The status is not WRITE_NOTE_STAGE!");
274         return false;
275     }
276     if (coreDumpThread_.targetPid == 0 || coreDumpThread_.targetTid == 0) {
277         DFXLOGE("targetPid or targetTid is 0!");
278         return false;
279     }
280 
281     NoteSegmentWriter note(mappedMemory_, currentPointer_, coreDumpThread_, maps_, keyRegs_);
282     note.SetKeyThreadData(coreDumpKeyThreadData_);
283     currentPointer_ = note.Write();
284     status_ = WriteStatus::WRITE_LOAD_SEGMENT_STAGE;
285     return true;
286 }
287 
WriteLoadSegment()288 bool CoreDumpService::WriteLoadSegment()
289 {
290     DFXLOGI("Begin to Write load segment");
291     if (status_ != WriteStatus::WRITE_LOAD_SEGMENT_STAGE) {
292         DFXLOGE("The status is not WRITE_SEGMENT_STAGE!");
293         return false;
294     }
295     if (coreDumpThread_.vmPid == 0) {
296         DFXLOGE("vmPid is 0!");
297         return false;
298     }
299     LoadSegmentWriter segment(mappedMemory_, currentPointer_, coreDumpThread_.vmPid, ePhnum_);
300     currentPointer_ = segment.Write();
301     status_ = WriteStatus::WRITE_SECTION_HEADER_STAGE;
302     return true;
303 }
304 
WriteSectionHeader()305 bool CoreDumpService::WriteSectionHeader()
306 {
307     DFXLOGI("Begin to Write section header");
308     if (status_ != WriteStatus::WRITE_SECTION_HEADER_STAGE) {
309         DFXLOGE("The status is not WRITE_SECTION_HEADER_STAGE!");
310         return false;
311     }
312     SectionHeaderTableWriter sectionHeaderTable(mappedMemory_, currentPointer_);
313     currentPointer_ = sectionHeaderTable.Write();
314     status_ = WriteStatus::STOP_STAGE;
315     return true;
316 }
317 
ElfHeaderFill(Elf64_Ehdr & eh,uint16_t ePhnum)318 void CoreDumpService::ElfHeaderFill(Elf64_Ehdr &eh, uint16_t ePhnum)
319 {
320     eh.e_ident[EI_MAG0] = ELFMAG0;
321     eh.e_ident[EI_MAG1] = ELFMAG1;
322     eh.e_ident[EI_MAG2] = ELFMAG2;
323     eh.e_ident[EI_MAG3] = ELFMAG3;
324     eh.e_ident[EI_CLASS] = ELFCLASS64;
325     eh.e_ident[EI_DATA] = ELFDATA2LSB;
326     eh.e_ident[EI_VERSION] = EV_CURRENT;
327     eh.e_ident[EI_OSABI] = ELFOSABI_NONE;
328     eh.e_ident[EI_ABIVERSION] = 0x00;
329     eh.e_ident[EI_PAD] = 0x00;
330     eh.e_ident[10] = 0x00; // 10
331     eh.e_ident[11] = 0x00; // 11
332     eh.e_ident[12] = 0x00; // 12
333     eh.e_ident[13] = 0x00; // 13
334     eh.e_ident[14] = 0x00; // 14
335     eh.e_ident[15] = 0x00; // 15
336     eh.e_type = ET_CORE;
337     eh.e_machine = EM_AARCH64;
338     eh.e_version = EV_CURRENT;
339     eh.e_entry = 0x00;
340     eh.e_phoff = sizeof(Elf64_Ehdr);
341     eh.e_shoff = sizeof(Elf64_Shdr);
342     eh.e_flags = 0x00;
343     eh.e_ehsize = sizeof(Elf64_Ehdr);
344     eh.e_phentsize = sizeof(Elf64_Phdr);
345     eh.e_phnum = ePhnum;
346     eh.e_shentsize = sizeof(Elf64_Shdr);
347     eh.e_shnum = ePhnum + ARG2;
348     eh.e_shstrndx = ePhnum + 1;
349 }
350 
IsDoCoredump()351 bool CoreDumpService::IsDoCoredump()
352 {
353     if (VerifyTrustlist() || (IsHwasanCoredumpEnabled() && isHwasanHap_)) {
354         return true;
355     }
356     DFXLOGE("The bundleName %{public}s is not in whitelist or hwasan coredump disable", bundleName_.c_str());
357     return false;
358 }
359 
IsCoredumpAllowed(const ProcessDumpRequest & request)360 bool CoreDumpService::IsCoredumpAllowed(const ProcessDumpRequest& request)
361 {
362     if (IsCoredumpSignal(request) || (request.siginfo.si_signo == SIGABRT && IsHwasanCoredumpEnabled())) {
363         return true;
364     }
365     return false;
366 }
367 
GetKeyThreadData(const ProcessDumpRequest & request)368 bool CoreDumpService::GetKeyThreadData(const ProcessDumpRequest& request)
369 {
370     pid_t tid = request.tid;
371     if (tid == 0) {
372         DFXLOGE("The keythread tid is 0, not to get keythread data");
373         return false;
374     }
375 
376     UserPacMask ntUserPacMask;
377     NoteSegmentWriter::GetRegset(tid, NT_ARM_PAC_MASK, ntUserPacMask);
378     coreDumpKeyThreadData_.ntUserPacMask = ntUserPacMask;
379 
380     struct user_fpsimd_struct ntFpregset;
381     if (NoteSegmentWriter::GetRegset(tid, NT_FPREGSET, ntFpregset)) {
382         coreDumpKeyThreadData_.fpRegValid = 1;
383     } else {
384         coreDumpKeyThreadData_.fpRegValid = 0;
385     }
386     coreDumpKeyThreadData_.ntFpregset = ntFpregset;
387 
388     siginfo_t ntSiginfo;
389     NoteSegmentWriter::GetSiginfoCommon(ntSiginfo, tid);
390     coreDumpKeyThreadData_.ntSiginfo = ntSiginfo;
391 
392     prstatus_t ntPrstatus;
393     if (NoteSegmentWriter::GetSiginfoCommon(ntPrstatus.pr_info, tid)) {
394         coreDumpKeyThreadData_.prStatusValid = 1;
395     } else {
396         coreDumpKeyThreadData_.prStatusValid = 0;
397     }
398     coreDumpKeyThreadData_.ntPrstatus = ntPrstatus;
399     return true;
400 }
401 
CreateFileForCoreDump()402 int CoreDumpService::CreateFileForCoreDump()
403 {
404     bundleName_ = DumpUtils::GetSelfBundleName();
405     if (bundleName_.empty()) {
406         DFXLOGE("query bundleName fail");
407         return INVALID_FD;
408     }
409     if (!IsDoCoredump()) {
410         return INVALID_FD;
411     }
412     std::string logPath = GetCoredumpFilePath();
413     RegisterCancelCoredump(logPath);
414     StartCoredumpCb(coreDumpThread_.targetPid, getpid());
415     if (access(COREDUMP_DIR_PATH, F_OK) != 0) {
416         DFXLOGE("%{public}s is not exist, errno = %{public}d", COREDUMP_DIR_PATH, errno);
417         return INVALID_FD;
418     }
419 
420     int fd = OHOS_TEMP_FAILURE_RETRY(open(logPath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
421     if (fd == INVALID_FD) {
422         DFXLOGE("create %{public}s fail, errno = %{public}d", logPath.c_str(), errno);
423     } else {
424         DFXLOGI("create coredump path %{public}s succ", logPath.c_str());
425     }
426     return fd;
427 }
428 
VerifyTrustlist()429 bool CoreDumpService::VerifyTrustlist()
430 {
431     if (bundleName_.empty()) {
432         return false;
433     }
434     std::string whiteList = GetCoredumpWhiteList();
435     if (whiteList.find(bundleName_) != std::string::npos) {
436         return true;
437     }
438     return false;
439 }
440 
AdjustFileSize(int fd,uint64_t fileSize)441 bool CoreDumpService::AdjustFileSize(int fd, uint64_t fileSize)
442 {
443     if (fd == -1) {
444         DFXLOGE("fd is invalid, not to adjust file size");
445         return false;
446     }
447     if (fileSize == 0) {
448         DFXLOGE("filesize is 0, not to adjust file size");
449         return false;
450     }
451     if (ftruncate(fd, fileSize) == -1) {
452         DFXLOGE("ftruncate fail, errno:%{public}d", errno);
453         return false;
454     }
455     return true;
456 }
457 
GetCoreFileSize(pid_t pid)458 uint64_t CoreDumpService::GetCoreFileSize(pid_t pid)
459 {
460     uint64_t coreFileSize = 0;
461     if (pid == 0) {
462         return coreFileSize;
463     }
464     std::string path = "/proc/" + std::to_string(pid) + "/maps";
465     std::ifstream file(path);
466     if (!file.is_open()) {
467         DFXLOGE("open %{public}s fail", path.c_str());
468         return coreFileSize;
469     }
470 
471     std::string line;
472     uint16_t lineNumber = 0;
473     DumpMemoryRegions region;
474     while (getline(file, line)) {
475         if (!isHwasanHap_ && line.find("libclang_rt.hwasan.so") != std::string::npos) {
476             isHwasanHap_ = true;
477         }
478         lineNumber += 1;
479         ObtainDumpRegion(line, region);
480         maps_.push_back(region);
481         std::string pri(region.priority);
482         if (pri.find('r') == std::string::npos || pri.find('p') == std::string::npos) {
483             continue;
484         }
485         coreFileSize += region.memorySizeHex;
486     }
487 
488     file.close();
489     coreFileSize = coreFileSize + sizeof(Elf64_Ehdr) + (lineNumber + 1) * sizeof(Elf64_Phdr) +
490         (lineNumber + 2) * sizeof(Elf64_Shdr) + lineNumber * (sizeof(Elf64_Nhdr) + ARG100) + ARG1000; // 2
491     DFXLOGI("The estimated corefile size is: %{public}ld, is hwasan hap %{public}d", coreFileSize, isHwasanHap_);
492     return coreFileSize;
493 }
494 
UnlinkFile(const std::string & logPath)495 bool CoreDumpService::UnlinkFile(const std::string &logPath)
496 {
497     if (logPath.empty()) {
498         return false;
499     }
500     if (unlink(logPath.c_str()) != 0) {
501         DFXLOGI("unlink file(%{public}s) fail, errno:%{public}d", logPath.c_str(), errno);
502         return false;
503     }
504     DFXLOGI("unlink file(%{public}s) success", logPath.c_str());
505     return true;
506 }
507 
GetCoredumpFileName()508 std::string CoreDumpService::GetCoredumpFileName()
509 {
510     if (bundleName_.empty()) {
511         return "";
512     }
513     return bundleName_ + ".dmp";
514 }
515 
GetCoredumpFilePath()516 std::string CoreDumpService::GetCoredumpFilePath()
517 {
518     if (bundleName_.empty()) {
519         return "";
520     }
521     return std::string(COREDUMP_DIR_PATH) + "/" + GetCoredumpFileName();
522 }
523 
ObtainDumpRegion(std::string & line,DumpMemoryRegions & region)524 void CoreDumpService::ObtainDumpRegion(std::string &line, DumpMemoryRegions &region)
525 {
526     auto ret = sscanf_s(line.c_str(), "%[^-\n]-%[^ ] %s %s %s %u %[^\n]",
527         region.start, sizeof(region.start),
528         region.end, sizeof(region.end),
529         region.priority, sizeof(region.priority),
530         region.offset, sizeof(region.offset),
531         region.dev, sizeof(region.dev),
532         &(region.inode),
533         region.pathName, sizeof(region.pathName));
534     if (ret != 7) { // 7
535         region.pathName[0] = '\0';
536     }
537 
538     region.startHex = strtoul(region.start, nullptr, ARG16);
539     region.endHex = strtoul(region.end, nullptr, ARG16);
540     region.offsetHex = strtoul(region.offset, nullptr, ARG16);
541     region.memorySizeHex = region.endHex - region.startHex;
542 }
543 } // namespace HiviewDFX
544 } // namespace OHOS
545 #endif