Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(mach headers): refactor to linked list to avoid threading issues #728

Merged
merged 6 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,12 @@

#import "BugsnagPlatformConditional.h"

#import <mach-o/dyld.h>
#import <execinfo.h>
#import "BSG_KSCrashAdvanced.h"

#import "BSG_KSCrashC.h"
#import "BSG_KSJSONCodecObjC.h"
#import "BSG_KSSingleton.h"
#import "BSG_KSMachHeaders.h"
#import "NSError+BSG_SimpleConstructor.h"

//#define BSG_KSLogger_LocalLevel TRACE
Expand Down Expand Up @@ -234,8 +232,6 @@ - (NSString *)stateFilePath {
}

- (BOOL)install {
// Maintain a cache of info about dynamically loaded binary images
[self listenForLoadedBinaries];

_handlingCrashTypes = bsg_kscrash_install(
[self.crashReportPath UTF8String], [self.recrashReportPath UTF8String],
Expand Down Expand Up @@ -271,21 +267,6 @@ - (BOOL)install {
return true;
}

/**
* Set up listeners for un/loaded frameworks. Maintaining our own list of framework Mach
* headers means that we avoid potential deadlock situations where we try and suspend
* lock-holding threads prior to loading mach headers as part of our normal event handling
* behaviour.
*/
tomlongridge marked this conversation as resolved.
Show resolved Hide resolved
- (void)listenForLoadedBinaries {
bsg_check_unfair_lock_support();
bsg_initialise_mach_binary_headers(BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE);

// Note: Access to DYLD's binary image store is guarded by locks.
_dyld_register_func_for_remove_image(&bsg_mach_binary_image_removed);
_dyld_register_func_for_add_image(&bsg_mach_binary_image_added);
}

- (void)sendAllReports {
[self.crashReportStore pruneFilesLeaving:self.maxStoredReports];

Expand Down
5 changes: 4 additions & 1 deletion Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "BSG_KSCrashReport.h"
#include "BSG_KSCrashSentry_User.h"
#include "BSG_KSMach.h"
#include "BSG_KSMachHeaders.h"
#include "BSG_KSObjC.h"
#include "BSG_KSString.h"
#include "BSG_KSSystemInfoC.h"
Expand Down Expand Up @@ -97,7 +98,9 @@ BSG_KSCrashType bsg_kscrash_install(const char *const crashReportFilePath,
BSG_KSLOG_DEBUG("Installing crash reporter.");

BSG_KSCrash_Context *context = crashContext();


bsg_mach_headers_initialize();

if (bsg_g_installed) {
BSG_KSLOG_DEBUG("Crash reporter already installed.");
return context->config.handlingCrashTypes;
Expand Down
29 changes: 13 additions & 16 deletions Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c
Original file line number Diff line number Diff line change
Expand Up @@ -1104,23 +1104,21 @@ int bsg_kscrw_i_threadIndex(const thread_t thread) {
*
* @param key The object key, if needed.
*
* @param index Cached info about the binary image.
* @param img Cached info about the binary image.
*/
void bsg_kscrw_i_writeBinaryImage(const BSG_KSCrashReportWriter *const writer,
const char *const key,
const uint32_t index)
const BSG_Mach_Header_Info *img)
{
BSG_Mach_Binary_Image_Info info = *bsg_dyld_get_image_info(index);

writer->beginObject(writer, key);
{
writer->addUIntegerElement(writer, BSG_KSCrashField_ImageAddress, (uintptr_t)info.header);
writer->addUIntegerElement(writer, BSG_KSCrashField_ImageVmAddress, info.imageVmAddr);
writer->addUIntegerElement(writer, BSG_KSCrashField_ImageSize, info.imageSize);
writer->addStringElement(writer, BSG_KSCrashField_Name, info.name);
writer->addUUIDElement(writer, BSG_KSCrashField_UUID, info.uuid);
writer->addIntegerElement(writer, BSG_KSCrashField_CPUType, info.header->cputype);
writer->addIntegerElement(writer, BSG_KSCrashField_CPUSubType, info.header->cpusubtype);
writer->addUIntegerElement(writer, BSG_KSCrashField_ImageAddress, (uintptr_t)img->header);
writer->addUIntegerElement(writer, BSG_KSCrashField_ImageVmAddress, img->imageVmAddr);
writer->addUIntegerElement(writer, BSG_KSCrashField_ImageSize, img->imageSize);
writer->addStringElement(writer, BSG_KSCrashField_Name, img->name);
writer->addUUIDElement(writer, BSG_KSCrashField_UUID, img->uuid);
writer->addIntegerElement(writer, BSG_KSCrashField_CPUType, img->header->cputype);
writer->addIntegerElement(writer, BSG_KSCrashField_CPUSubType, img->header->cpusubtype);
}
writer->endContainer(writer);
}
Expand All @@ -1134,13 +1132,12 @@ void bsg_kscrw_i_writeBinaryImage(const BSG_KSCrashReportWriter *const writer,
void bsg_kscrw_i_writeBinaryImages(const BSG_KSCrashReportWriter *const writer,
const char *const key)
{
const uint32_t imageCount = bsg_dyld_image_count();

writer->beginArray(writer, key);
{
for (uint32_t iImg = 0; iImg < imageCount; iImg++) {
// Threads are suspended at this point; no need to synchronise/lock
bsg_kscrw_i_writeBinaryImage(writer, NULL, iImg);
for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = img->next) {
if (!img->removed) {
bsg_kscrw_i_writeBinaryImage(writer, NULL, img);
}
}
}
writer->endContainer(writer);
Expand Down
3 changes: 2 additions & 1 deletion Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#import "BSG_KSSystemInfo.h"
#import "BSG_KSSystemInfoC.h"
#import "BSG_KSDynamicLinker.h"
#import "BSG_KSMachHeaders.h"
#import "BSG_KSJSONCodecObjC.h"
#import "BSG_KSMach.h"
#import "BSG_KSSysCtl.h"
Expand Down Expand Up @@ -259,7 +260,7 @@ + (NSString *)currentCPUArch {
* @return YES if the device is jailbroken.
*/
+ (BOOL)isJailbroken {
return bsg_ksdlimageNamed("MobileSubstrate", false) != UINT32_MAX;
return bsg_mach_headers_image_named("MobileSubstrate", false) != NULL;
}

/** Check if the current build is a debug build.
Expand Down
203 changes: 13 additions & 190 deletions Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,37 +32,13 @@
#include <mach-o/nlist.h>
#include <string.h>

uint32_t bsg_ksdlimageNamed(const char *const imageName, bool exactMatch) {
if (imageName != NULL) {
const uint32_t imageCount = bsg_dyld_image_count();

for (uint32_t iImg = 0; iImg < imageCount; iImg++) {
const char *name = bsg_dyld_get_image_name(iImg);
if (name == NULL) {
continue; // name is null if the index is out of range per dyld(3)
} else if (exactMatch) {
if (strcmp(name, imageName) == 0) {
return iImg;
}
} else {
if (strstr(name, imageName) != NULL) {
return iImg;
}
}
}
}
return UINT32_MAX;
}

const uint8_t *bsg_ksdlimageUUID(const char *const imageName, bool exactMatch) {
if (imageName != NULL) {
const uint32_t iImg = bsg_ksdlimageNamed(imageName, exactMatch);
if (iImg != UINT32_MAX) {
const struct mach_header *header = bsg_dyld_get_image_header(iImg);
if (header != NULL) {
uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header);
BSG_Mach_Header_Info *img = bsg_mach_headers_image_named(imageName, exactMatch);
if (img != NULL) {
uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(img->header);
if (cmdPtr != 0) {
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
for (uint32_t iCmd = 0; iCmd < img->header->ncmds; iCmd++) {
tomlongridge marked this conversation as resolved.
Show resolved Hide resolved
const struct load_command *loadCmd =
(struct load_command *)cmdPtr;
if (loadCmd->cmd == LC_UUID) {
Expand All @@ -75,132 +51,37 @@ const uint8_t *bsg_ksdlimageUUID(const char *const imageName, bool exactMatch) {
}
}
}
}
return NULL;
}

uintptr_t bsg_ksdlfirstCmdAfterHeader(const struct mach_header *const header) {
if (header == NULL) {
return 0;
}
switch (header->magic) {
case MH_MAGIC:
case MH_CIGAM:
return (uintptr_t)(header + 1);
case MH_MAGIC_64:
case MH_CIGAM_64:
return (uintptr_t)(((struct mach_header_64 *)header) + 1);
default:
// Header is corrupt
return 0;
}
}

uint32_t bsg_ksdlimageIndexContainingAddress(const uintptr_t address) {
const uint32_t imageCount = bsg_dyld_image_count();
const struct mach_header *header = 0;

for (uint32_t iImg = 0; iImg < imageCount; iImg++) {
header = bsg_dyld_get_image_header(iImg);
if (header != NULL) {
// Look for a segment command with this address within its range.
uintptr_t addressWSlide =
address - bsg_dyld_get_image_vmaddr_slide(iImg);
uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header);
if (cmdPtr == 0) {
continue;
}
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
const struct load_command *loadCmd =
(struct load_command *)cmdPtr;
if (loadCmd->cmd == LC_SEGMENT) {
const struct segment_command *segCmd =
(struct segment_command *)cmdPtr;
if (addressWSlide >= segCmd->vmaddr &&
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
return iImg;
}
} else if (loadCmd->cmd == LC_SEGMENT_64) {
const struct segment_command_64 *segCmd =
(struct segment_command_64 *)cmdPtr;
if (addressWSlide >= segCmd->vmaddr &&
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
return iImg;
}
}
cmdPtr += loadCmd->cmdsize;
}
}
}
return UINT_MAX;
}

uintptr_t bsg_ksdlsegmentBaseOfImageIndex(const uint32_t idx) {
const struct mach_header *header = bsg_dyld_get_image_header(idx);
if (header == NULL) {
return 0;
}

// Look for a segment command and return the file image address.
uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header);
if (cmdPtr == 0) {
return 0;
}
for (uint32_t i = 0; i < header->ncmds; i++) {
const struct load_command *loadCmd = (struct load_command *)cmdPtr;
if (loadCmd->cmd == LC_SEGMENT) {
const struct segment_command *segmentCmd =
(struct segment_command *)cmdPtr;
if (strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) {
return segmentCmd->vmaddr - segmentCmd->fileoff;
}
} else if (loadCmd->cmd == LC_SEGMENT_64) {
const struct segment_command_64 *segmentCmd =
(struct segment_command_64 *)cmdPtr;
if (strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) {
return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff);
}
}
cmdPtr += loadCmd->cmdsize;
}

return 0;
}

bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) {
info->dli_fname = NULL;
info->dli_fbase = NULL;
info->dli_sname = NULL;
info->dli_saddr = NULL;

const uint32_t idx = bsg_ksdlimageIndexContainingAddress(address);
if (idx == UINT_MAX) {
return false;
}
const struct mach_header *header = bsg_dyld_get_image_header(idx);
if (header == NULL) {
BSG_Mach_Header_Info *image = bsg_mach_headers_image_at_address(address);
if (image == NULL) {
return false;
}
const uintptr_t imageVMAddrSlide =
bsg_dyld_get_image_vmaddr_slide(idx);
const uintptr_t addressWithSlide = address - imageVMAddrSlide;
const uintptr_t addressWithSlide = address - image->slide;
const uintptr_t segmentBase =
bsg_ksdlsegmentBaseOfImageIndex(idx) + imageVMAddrSlide;
bsg_mach_headers_image_at_base_of_image_index(image->header) + image->slide;
if (segmentBase == 0) {
return false;
}

info->dli_fname = bsg_dyld_get_image_name(idx);
info->dli_fbase = (void *)header;
info->dli_fname = image->name;
info->dli_fbase = (void *)image->header;

// Find symbol tables and get whichever symbol is closest to the address.
const BSG_STRUCT_NLIST *bestMatch = NULL;
uintptr_t bestDistance = ULONG_MAX;
uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header);
uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(image->header);
if (cmdPtr == 0) {
return false;
}
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
for (uint32_t iCmd = 0; iCmd < image->header->ncmds; iCmd++) {
const struct load_command *loadCmd = (struct load_command *)cmdPtr;
if (loadCmd->cmd == LC_SYMTAB) {
const struct symtab_command *symtabCmd =
Expand All @@ -223,7 +104,7 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) {
}
if (bestMatch != NULL) {
info->dli_saddr =
(void *)(bestMatch->n_value + imageVMAddrSlide);
(void *)(bestMatch->n_value + image->slide);
info->dli_sname = (char *)((intptr_t)stringTable +
(intptr_t)bestMatch->n_un.n_strx);
if (*info->dli_sname == '_') {
Expand All @@ -243,61 +124,3 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) {
return true;
}

const void *bsg_ksdlgetSymbolAddrInImage(uint32_t imageIdx,
const char *symbolName) {
const struct mach_header *header = bsg_dyld_get_image_header(imageIdx);
if (header == NULL) {
return NULL;
}
const uintptr_t imageVMAddrSlide =
bsg_dyld_get_image_vmaddr_slide(imageIdx);
const uintptr_t segmentBase =
bsg_ksdlsegmentBaseOfImageIndex(imageIdx) + imageVMAddrSlide;
if (segmentBase == 0) {
return NULL;
}
uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header);
if (cmdPtr == 0) {
return NULL;
}
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
const struct load_command *loadCmd = (struct load_command *)cmdPtr;
if (loadCmd->cmd == LC_SYMTAB) {
const struct symtab_command *symtabCmd =
(struct symtab_command *)cmdPtr;
const BSG_STRUCT_NLIST *symbolTable =
(BSG_STRUCT_NLIST *)(segmentBase + symtabCmd->symoff);
const uintptr_t stringTable = segmentBase + symtabCmd->stroff;

for (uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++) {
// If n_value is 0, the symbol refers to an external object.
if (symbolTable[iSym].n_value != 0) {
const char *sname =
(char *)((intptr_t)stringTable +
(intptr_t)symbolTable[iSym].n_un.n_strx);
if (*sname == '_') {
sname++;
}
if (strcmp(sname, symbolName) == 0) {
return (void *)(symbolTable[iSym].n_value +
imageVMAddrSlide);
}
}
}
}
cmdPtr += loadCmd->cmdsize;
}
return NULL;
}

const void *bsg_ksdlgetSymbolAddrInAnyImage(const char *symbolName) {
const uint32_t imageCount = bsg_dyld_image_count();

for (uint32_t iImg = 0; iImg < imageCount; iImg++) {
const void *symbolAddr = bsg_ksdlgetSymbolAddrInImage(iImg, symbolName);
if (symbolAddr != NULL) {
return symbolAddr;
}
}
return NULL;
}
Loading