this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

darling-coredump: Many improvements and fixes

+125 -14
+16
src/hosttools/CMakeLists.txt
··· 1 1 project(hosttools) 2 2 3 + set(DARLING_COREDUMP_SANITIZE OFF) 4 + 3 5 add_executable(darling-coredump 4 6 src/coredump/main.c 5 7 ) ··· 13 15 ) 14 16 15 17 install(TARGETS darling-coredump DESTINATION bin) 18 + 19 + if (DARLING_COREDUMP_SANITIZE) 20 + target_compile_options(darling-coredump PRIVATE 21 + -fsanitize=address,undefined 22 + -fsanitize-recover=all 23 + -fno-omit-frame-pointer 24 + -g 25 + -Og 26 + ) 27 + target_link_options(darling-coredump PRIVATE 28 + -fsanitize=address,undefined 29 + -fsanitize-recover=all 30 + ) 31 + endif()
+109 -14
src/hosttools/src/coredump/main.c
··· 33 33 size_t file_size; 34 34 uint8_t protection; 35 35 bool valid; 36 + uint64_t expected_offset; 36 37 }; 37 38 38 39 struct nt_file_header { ··· 65 66 }; 66 67 67 68 struct thread_info { 68 - const struct nt_prstatus* prstatus; 69 + struct nt_prstatus* prstatus; 69 70 }; 70 71 71 72 struct coredump_params { ··· 80 81 size_t input_notes_size; 81 82 struct vm_area* vm_areas; 82 83 size_t vm_area_count; 83 - const struct nt_file_header* nt_file; 84 + struct nt_file_header* nt_file; 84 85 const char** nt_file_filenames; 85 86 struct thread_info* thread_infos; 86 87 size_t thread_info_count; 87 88 size_t written; 88 89 const char* prefix; 89 90 size_t prefix_length; 91 + const char* main_executable_path; 92 + size_t main_executable_path_length; 90 93 }; 91 94 92 95 static char default_output_name[4096]; ··· 223 226 224 227 for (const Elf64_Nhdr* note_header = cprm.input_notes; note_header < (const Elf64_Nhdr*)((const char*)cprm.input_notes + cprm.input_notes_size); note_header = find_next_note(note_header)) { 225 228 if (note_header->n_type == NT_FILE) { 226 - cprm.nt_file = note_data(note_header); 229 + // allocate a copy for alignment purposes 230 + cprm.nt_file = malloc(note_header->n_descsz); 231 + if (!cprm.nt_file) { 232 + perror("malloc"); 233 + return 1; 234 + } 235 + memcpy(cprm.nt_file, note_data(note_header), note_header->n_descsz); 227 236 228 237 cprm.nt_file_filenames = malloc(cprm.nt_file->count * sizeof(const char*)); 229 238 if (!cprm.nt_file_filenames) { ··· 257 266 size_t thread_info_index = 0; 258 267 for (const Elf64_Nhdr* note_header = cprm.input_notes; note_header < (const Elf64_Nhdr*)((const char*)cprm.input_notes + cprm.input_notes_size); note_header = find_next_note(note_header)) { 259 268 if (note_header->n_type == NT_PRSTATUS) { 260 - cprm.thread_infos[thread_info_index++].prstatus = note_data(note_header); 269 + // allocate a copy for alignment purposes 270 + struct nt_prstatus* prstatus = malloc(note_header->n_descsz); 271 + if (!prstatus) { 272 + perror("malloc"); 273 + return 1; 274 + } 275 + cprm.thread_infos[thread_info_index++].prstatus = prstatus; 276 + memcpy(prstatus, note_data(note_header), note_header->n_descsz); 261 277 } else { 262 278 continue; 263 279 } 264 280 } 265 281 266 282 // determine if we have extra mappings in NT_FILE that aren't present in the program headers (gcore tends to do this) 283 + // also try to determine which file is our main executable 267 284 for (size_t i = 0; i < cprm.nt_file->count; ++i) { 268 285 const struct nt_file_entry* entry = &((const struct nt_file_entry*)((const char*)cprm.nt_file + sizeof(struct nt_file_header)))[i]; 269 286 const char* filename = cprm.nt_file_filenames[i]; 270 287 bool found = false; 271 288 289 + // try to determine if this is the main executable file 290 + if (strncmp(filename, "/dev/", sizeof("/dev/") - 1) != 0) { 291 + int tmpfd = open(filename, O_RDONLY); 292 + if (tmpfd >= 0) { 293 + struct mach_header_64 mh; 294 + if (read(tmpfd, &mh, sizeof(mh)) == sizeof(mh) && mh.filetype == MH_EXECUTE) { 295 + if (cprm.main_executable_path) { 296 + if (strcmp(cprm.main_executable_path, filename) != 0) { 297 + printf("Already had main executable (\"%s\") but found another? (\"%s\")\n", cprm.main_executable_path, filename); 298 + exit(EXIT_FAILURE); 299 + } 300 + } else { 301 + cprm.main_executable_path = filename; 302 + cprm.main_executable_path_length = strlen(cprm.main_executable_path); 303 + printf("Found main executable: %s\n", cprm.main_executable_path); 304 + } 305 + } 306 + close(tmpfd); 307 + } 308 + } 309 + 272 310 for (const Elf64_Phdr* program_header = cprm.input_program_headers; program_header < cprm.input_program_headers_end; program_header = (const Elf64_Phdr*)((const char*)program_header + cprm.input_header->e_phentsize)) { 273 311 if (program_header->p_type != PT_LOAD) { 274 312 continue; ··· 296 334 return 1; 297 335 } 298 336 337 + memset(cprm.vm_areas, 0, sizeof(*cprm.vm_areas) * cprm.vm_area_count); 338 + 299 339 // now load up the VM area array 300 340 size_t vm_area_index = 0; 301 341 for (const Elf64_Phdr* program_header = cprm.input_program_headers; program_header < cprm.input_program_headers_end; program_header = (const Elf64_Phdr*)((const char*)program_header + cprm.input_header->e_phentsize)) { ··· 306 346 vm_area->memory_address = program_header->p_vaddr; 307 347 vm_area->memory_size = program_header->p_memsz; 308 348 vm_area->file_offset = program_header->p_offset; 349 + 350 + if (!cprm.main_executable_path && vm_area->memory_address == 0) { 351 + // if this is PAGEZERO and we don't yet have a main executable registered, 352 + // the file containing this segment is most likely the main executable 353 + cprm.main_executable_path = vm_area->filename; 354 + cprm.main_executable_path_length = vm_area->filename_length; 355 + printf("Found main executable (maybe): %s\n", cprm.main_executable_path); 356 + } 309 357 310 358 vm_area->protection = 0; 311 359 if (program_header->p_flags & PF_R) { ··· 343 391 } 344 392 345 393 if (!vm_area->filename) { 346 - // ignore it if starts at 0; that's most likely the __PAGEZERO segment 347 - if (vm_area->memory_address != 0) { 348 - printf("warning: missing NT_FILE entry for zero-sized memory region 0x%zx-%zx (%lu bytes)\n", vm_area->memory_address, vm_area->memory_address + vm_area->memory_size, vm_area->memory_size); 394 + if (vm_area->protection == 0) { 395 + // if the area has no protection flags, it most likely wasn't included anywhere. 396 + // no need to warn. 397 + } else if (vm_area->memory_address == 0) { 398 + // ignore it if starts at 0; that's most likely the __PAGEZERO segment. 399 + // no need to warn. 400 + } else { 401 + // otherwise, warn. 402 + printf("warning: missing NT_FILE entry for memory region 0x%zx-%zx (%lu bytes)\n", vm_area->memory_address, vm_area->memory_address + vm_area->memory_size, vm_area->memory_size); 349 403 } 404 + vm_area->file_size = 0; 350 405 vm_area->valid = false; 351 406 } 352 407 } else { ··· 402 457 403 458 macho_coredump(&cprm); 404 459 405 - #warning TODO: cleanup 460 + // now let's clean-up 461 + 462 + for (size_t i = 0; i < cprm.thread_info_count; ++i) { 463 + free(cprm.thread_infos[i].prstatus); 464 + } 465 + 466 + free(cprm.thread_infos); 467 + free(cprm.vm_areas); 468 + free(cprm.nt_file_filenames); 469 + free(cprm.nt_file); 406 470 407 471 return 0; 408 472 }; ··· 429 493 return false; 430 494 #endif 431 495 return true; 496 + }; 497 + 498 + static uint64_t dump_offset_get(struct coredump_params* cprm) { 499 + return lseek(cprm->output_corefile, 0, SEEK_CUR); 432 500 }; 433 501 434 502 // the following coredump code has been imported from the LKM and adapted for use in userspace ··· 607 675 608 676 const int statesize = sizeof(x86_thread_state64_t) + sizeof(x86_float_state64_t) + sizeof(struct thread_flavor) * 2; 609 677 mh.sizeofcmds = segs * sizeof(struct segment_command_64) + threads * (sizeof(struct thread_command) + statesize); 678 + mh.flags = 0; 610 679 mh.reserved = 0; 611 680 681 + if (cprm->main_executable_path) { 682 + int tmpfd = open(cprm->main_executable_path, O_RDONLY); 683 + if (tmpfd >= 0) { 684 + struct mach_header_64 that_mh; 685 + if (read(tmpfd, &that_mh, sizeof(that_mh)) == sizeof(that_mh)) { 686 + mh.flags = that_mh.flags; 687 + } 688 + close(tmpfd); 689 + } 690 + } else { 691 + fprintf(stderr, "No main executable detected?\n"); 692 + } 693 + 612 694 if (!dump_emit(cprm, &mh, sizeof(mh))) 613 695 goto fail; 614 696 615 697 struct vm_area_struct* vma; 616 - uint32_t file_offset = mh.sizeofcmds + sizeof(mh); 698 + uint64_t file_offset = mh.sizeofcmds + sizeof(mh); 617 699 618 700 for (size_t i = 0; i < cprm->vm_area_count; ++i) { 619 - const struct vm_area* vma = &cprm->vm_areas[i]; 701 + struct vm_area* vma = &cprm->vm_areas[i]; 620 702 struct segment_command_64 sc; 621 703 622 704 sc.cmd = LC_SEGMENT_64; ··· 628 710 sc.vmsize = vma->memory_size; 629 711 sc.fileoff = file_offset; 630 712 631 - if (sc.vmaddr > 0) // avoid dumping the __PAGEZERO segment which may be really large 713 + vma->expected_offset = sc.fileoff; 714 + 715 + if (vma->valid) // avoid dumping the __PAGEZERO segment which may be really large 632 716 sc.filesize = vma->file_size; 633 717 else 634 718 sc.filesize = 0; ··· 711 795 const struct vm_area* vma = &cprm->vm_areas[i]; 712 796 unsigned long addr; 713 797 714 - if (vma->memory_address == 0) 715 - continue; // skip __PAGEZERO dumping 798 + uint64_t curr = dump_offset_get(cprm); 799 + if (curr != vma->expected_offset) { 800 + fprintf(stderr, "Expected offset %lx, got offset %lx\n", vma->expected_offset, curr); 801 + exit(EXIT_FAILURE); 802 + } 716 803 717 804 if (!vma->valid) { 718 805 if (!dump_skip(cprm, vma->file_size)) 719 806 exit(EXIT_FAILURE); 720 807 } else { 721 808 if (vma->filename) { 809 + if (strncmp(vma->filename, "/dev/", sizeof("/dev/") - 1) == 0) { 810 + printf("Warning: skipping device \"%s\"\n", vma->filename); 811 + if (!dump_skip(cprm, vma->file_size)) { 812 + exit(EXIT_FAILURE); 813 + } 814 + continue; 815 + } 816 + 722 817 int fd = -1; 723 818 fd = open(vma->filename, O_RDONLY); 724 819 if (fd < 0 && vma->filename_length >= cprm->prefix_length && strncmp(vma->filename, cprm->prefix, cprm->prefix_length) == 0) { ··· 738 833 } 739 834 740 835 uintptr_t aligned_offset = round_down_pow2(vma->file_offset, sysconf(_SC_PAGESIZE)); 741 - size_t aligned_size = round_up_pow2(vma->file_size, _SC_PAGESIZE); 836 + size_t aligned_size = round_up_pow2(vma->file_size, sysconf(_SC_PAGESIZE)); 742 837 void* mapping = mmap(NULL, aligned_size, PROT_READ, MAP_PRIVATE, fd, aligned_offset); 743 838 if (mapping == MAP_FAILED) { 744 839 perror("mmap");