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 ®ion)
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