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

Fix DYLD lock mechanism preventing compilation on iOS <10 #675

Merged
merged 7 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions Bugsnag.podspec.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Bugsnag",
"version": "5.23.2",
"version": "5.23.3",
"summary": "Cocoa notifier for SDK for bugsnag.com",
"homepage": "https://bugsnag.com",
"license": "MIT",
Expand All @@ -9,7 +9,7 @@
},
"source": {
"git": "https://github.com/bugsnag/bugsnag-cocoa.git",
"tag": "v5.23.2"
"tag": "v5.23.3"
},
"frameworks": ["Foundation", "SystemConfiguration"],
"libraries": [
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

## 5.23.3 (2020-06-05)

## Bug Fixes

* Fix DYLD lock mechanism preventing compilation on iOS <10.
[#675](https://github.com/bugsnag/bugsnag-cocoa/pull/675)

## 5.23.2 (2020-05-13)

## Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion Source/BugsnagNotifier.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
#import <AppKit/AppKit.h>
#endif

NSString *const NOTIFIER_VERSION = @"5.23.2";
NSString *const NOTIFIER_VERSION = @"5.23.3";
NSString *const NOTIFIER_URL = @"https://github.com/bugsnag/bugsnag-cocoa";
NSString *const BSTabCrash = @"crash";
NSString *const BSAttributeDepth = @"depth";
Expand Down
4 changes: 2 additions & 2 deletions Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,10 @@ - (BOOL)install {
* behaviour.
*/
- (void)listenForLoadedBinaries {
bsg_check_unfair_lock_support();
bsg_initialise_mach_binary_headers(BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE);

// Note: Internally, access to DYLD's binary image store is guarded by an OSSpinLock. We therefore don't need to
// add additional guards around our access.
// 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);
}
Expand Down
57 changes: 19 additions & 38 deletions Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#define BSG_KSMachHeaders_h

#import <mach/machine.h>
#import <os/lock.h>
#import <libkern/OSAtomic.h>

/**
* An encapsulation of the Mach header - either 64 or 32 bit, along with some additional information required for
Expand All @@ -36,54 +38,28 @@ typedef struct {

static BSG_Mach_Binary_Images bsg_mach_binary_images;

// MARK: - Locking

/**
* An OS-version-specific lock, used to synchronise access to the array of binary image info.
* An OS-version-specific lock, used to synchronise access to the array of binary image info. A combination of
* compile-time determination of the OS and and run-time determination of the OS version is used to ensure that
* the correct lock mechanism is used.
*
* os_unfair_lock is available from specific OS versions onwards:
* https://developer.apple.com/documentation/os/os_unfair_lock
*
* It deprecates OSSpinLock:
* https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/spinlock.3.html
*
* The #defined BSG_DYLD_CACHE_LOCK/UNLOCK avoid spurious warnings on later OSs.
* The imported headers have specific version info: <os/lock.h> and <libkern/OSAtomic.h>
*/

#if defined(__IPHONE_10_0) || defined(__MAC_10_12) || defined(__TVOS_10_0) || defined(__WATCHOS_3_0)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"

#import <os/lock.h>
static os_unfair_lock bsg_mach_binary_images_access_lock = OS_UNFAIR_LOCK_INIT;

#ifndef BSG_DYLD_CACHE_LOCK
#define BSG_DYLD_CACHE_LOCK \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \
os_unfair_lock_lock(&bsg_mach_binary_images_access_lock); \
_Pragma("clang diagnostic pop")
#endif

#ifndef BSG_DYLD_CACHE_UNLOCK
#define BSG_DYLD_CACHE_UNLOCK \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \
os_unfair_lock_unlock(&bsg_mach_binary_images_access_lock); \
_Pragma("clang diagnostic pop")
#endif

#pragma clang diagnostic pop
#else
#import <libkern/OSAtomic.h>
static OSSpinLock bsg_mach_binary_images_access_lock = OS_SPINLOCK_INIT;

#ifndef BSG_DYLD_CACHE_LOCK
#define BSG_DYLD_CACHE_LOCK OSSpinLockLock(&bsg_mach_binary_images_access_lock);
#endif

#ifndef BSG_DYLD_CACHE_UNLOCK
#define BSG_DYLD_CACHE_UNLOCK OSSpinLockUnlock(&bsg_mach_binary_images_access_lock);
#endif
#endif
void bsg_spin_lock(void);
void bsg_spin_unlock(void);
void bsg_unfair_lock(void);
void bsg_unfair_unlock(void);
void bsg_dyld_cache_lock(void);
void bsg_dyld_cache_unlock(void);

// MARK: - Replicate the DYLD API

Expand Down Expand Up @@ -126,4 +102,9 @@ void bsg_mach_binary_image_removed(const struct mach_header *mh, intptr_t slide)
*/
BSG_Mach_Binary_Images *bsg_initialise_mach_binary_headers(uint32_t initialSize);

/**
* Determines whether the OS supports unfair locks or not.
*/
void bsg_check_unfair_lock_support(void);

#endif /* BSG_KSMachHeaders_h */
99 changes: 94 additions & 5 deletions Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,88 @@
#import "BSG_KSDynamicLinker.h"
#import "BSG_KSMachHeaders.h"

// MARK: - Locking

// Pragma's hide unavoidable (and expected) deprecation/unavailable warnings
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wunguarded-availability\"")
static os_unfair_lock bsg_mach_binary_images_access_lock_unfair = OS_UNFAIR_LOCK_INIT;
_Pragma("clang diagnostic pop")

_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
static OSSpinLock bsg_mach_binary_images_access_lock_spin = OS_SPINLOCK_INIT;
_Pragma("clang diagnostic pop")

static BOOL bsg_unfair_lock_supported;

// Lock helpers. These use bulky Pragmas to hide warnings so are in their own functions for clarity.

void bsg_spin_lock() {
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
OSSpinLockLock(&bsg_mach_binary_images_access_lock_spin);
_Pragma("clang diagnostic pop")
}

void bsg_spin_unlock() {
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
OSSpinLockUnlock(&bsg_mach_binary_images_access_lock_spin);
_Pragma("clang diagnostic pop")
}

void bsg_unfair_lock() {
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wunguarded-availability\"")
os_unfair_lock_lock(&bsg_mach_binary_images_access_lock_unfair);
_Pragma("clang diagnostic pop")
}

void bsg_unfair_unlock() {
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wunguarded-availability\"")
os_unfair_lock_unlock(&bsg_mach_binary_images_access_lock_unfair);
_Pragma("clang diagnostic pop")
}

// Lock and unlock sections of code

void bsg_dyld_cache_lock() {
if (bsg_unfair_lock_supported) {
bsg_unfair_lock();
} else {
bsg_spin_lock();
}
}

void bsg_dyld_cache_unlock() {
if (bsg_unfair_lock_supported) {
bsg_unfair_unlock();
} else {
bsg_spin_unlock();
}
}

BOOL BSGIsUnfairLockSupported(NSProcessInfo *processInfo) {
NSOperatingSystemVersion minSdk = {0,0,0};
#if TARGET_OS_IOS
minSdk.majorVersion = 10;
#elif TARGET_OS_OSX
minSdk.majorVersion = 10;
minSdk.minorVersion = 12;
#elif TARGET_OS_TV
minSdk.majorVersion = 10;
#elif TARGET_OS_WATCH
minSdk.majorVersion = 3;
#endif
return [processInfo isOperatingSystemAtLeastVersion:minSdk];
}

void bsg_check_unfair_lock_support() {
bsg_unfair_lock_supported = BSGIsUnfairLockSupported([NSProcessInfo processInfo]);
}

// MARK: - Replicate the DYLD API

uint32_t bsg_dyld_image_count(void) {
Expand Down Expand Up @@ -53,7 +135,7 @@ intptr_t bsg_dyld_get_image_vmaddr_slide(uint32_t imageIndex) {
*/
void bsg_add_mach_binary_image(BSG_Mach_Binary_Image_Info element) {

BSG_DYLD_CACHE_LOCK
bsg_dyld_cache_lock();

// Expand array if necessary. We're slightly paranoid here. An OOM is likely to be indicative of bigger problems
// but we should still do *our* best not to crash the app.
Expand All @@ -69,15 +151,15 @@ void bsg_add_mach_binary_image(BSG_Mach_Binary_Image_Info element) {
}
else {
// Exit early, don't expand the array, don't store the header info and unlock
BSG_DYLD_CACHE_UNLOCK
bsg_dyld_cache_unlock();
return;
}
}

// Store the value, increment the number of used elements
bsg_mach_binary_images.contents[bsg_mach_binary_images.used++] = element;

BSG_DYLD_CACHE_UNLOCK
bsg_dyld_cache_unlock();
}

/**
Expand All @@ -87,7 +169,7 @@ void bsg_add_mach_binary_image(BSG_Mach_Binary_Image_Info element) {
*/
void bsg_remove_mach_binary_image(uint64_t imageVmAddr) {

BSG_DYLD_CACHE_LOCK
bsg_dyld_cache_lock();

for (uint32_t i=0; i<bsg_mach_binary_images.used; i++) {
BSG_Mach_Binary_Image_Info item = bsg_mach_binary_images.contents[i];
Expand All @@ -104,9 +186,16 @@ void bsg_remove_mach_binary_image(uint64_t imageVmAddr) {
}
}

BSG_DYLD_CACHE_UNLOCK
bsg_dyld_cache_unlock();
}

/**
* Create an empty array with initial capacity to hold Mach header info.
*
* @param initialSize The initial array capacity
*
* @returns A struct for holding Mach binary image info
*/
BSG_Mach_Binary_Images *bsg_initialise_mach_binary_headers(uint32_t initialSize) {
bsg_mach_binary_images.contents = (BSG_Mach_Binary_Image_Info *)malloc(initialSize * sizeof(BSG_Mach_Binary_Image_Info));
bsg_mach_binary_images.used = 0;
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.23.2
5.23.3