Skip to content

Commit

Permalink
Merge pull request #1012 from bugsnag/release-v6.6.4
Browse files Browse the repository at this point in the history
Release v6.6.4
  • Loading branch information
nickdowell authored Feb 24, 2021
2 parents a3f4a39 + c7ed295 commit 6476eb4
Show file tree
Hide file tree
Showing 16 changed files with 360 additions and 49 deletions.
4 changes: 2 additions & 2 deletions .jazzy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ author_url: "https://www.bugsnag.com"
author: "Bugsnag Inc"
clean: false # avoid deleting docs/.git
framework_root: "Bugsnag"
github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.6.3/Bugsnag"
github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.6.4/Bugsnag"
github_url: "https://github.com/bugsnag/bugsnag-cocoa"
hide_documentation_coverage: true
module: "Bugsnag"
module_version: "6.6.3"
module_version: "6.6.4"
objc: true
output: "docs"
readme: "README.md"
Expand Down
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": "6.6.3",
"version": "6.6.4",
"summary": "The Bugsnag crash reporting framework for Apple platforms.",
"homepage": "https://bugsnag.com",
"license": "MIT",
Expand All @@ -9,7 +9,7 @@
},
"source": {
"git": "https://github.com/bugsnag/bugsnag-cocoa.git",
"tag": "v6.6.3"
"tag": "v6.6.4"
},
"frameworks": [
"Foundation",
Expand Down
2 changes: 2 additions & 0 deletions Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@
CBCF77A125010648004AF22A /* BSGJSONSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGJSONSerialization.h; sourceTree = "<group>"; };
CBCF77A225010648004AF22A /* BSGJSONSerialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGJSONSerialization.m; sourceTree = "<group>"; };
CBCF77AA250142E0004AF22A /* BSGJSONSerializationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGJSONSerializationTests.m; sourceTree = "<group>"; };
CBD50F6025DA72EC00DF0314 /* BSG_Jailbreak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSG_Jailbreak.h; sourceTree = "<group>"; };
CBE9062825A34DAB0045B965 /* BSGStorageMigratorV0V1.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGStorageMigratorV0V1.h; sourceTree = "<group>"; };
CBE9062925A34DAB0045B965 /* BSGStorageMigratorV0V1.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGStorageMigratorV0V1.m; sourceTree = "<group>"; };
E701FA9E2490EF4A008D842F /* BugsnagApiValidationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagApiValidationTest.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1540,6 +1541,7 @@
0089692F2486DAD000DC48C2 /* BSG_KSSystemInfo.h */,
0089694C2486DAD000DC48C2 /* BSG_KSSystemInfo.m */,
008969312486DAD000DC48C2 /* BSG_KSSystemInfoC.h */,
CBD50F6025DA72EC00DF0314 /* BSG_Jailbreak.h */,
0089693A2486DAD000DC48C2 /* Sentry */,
008969012486DAD000DC48C2 /* Tools */,
);
Expand Down
1 change: 1 addition & 0 deletions Bugsnag/BugsnagSystemState.m
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ id blankIfNil(id value) {
NSMutableDictionary *device = [NSMutableDictionary new];
device[SYSTEMSTATE_DEVICE_BOOT_TIME] = [BSG_RFC3339DateTool stringFromDate:systemInfo[@BSG_KSSystemField_BootTime]];
device[@"id"] = systemInfo[@BSG_KSSystemField_DeviceAppHash];
device[@"jailbroken"] = systemInfo[@BSG_KSSystemField_Jailbroken];
device[@"osBuild"] = systemInfo[@BSG_KSSystemField_OSVersion];
device[@"osVersion"] = systemInfo[@BSG_KSSystemField_SystemVersion];
device[@"osName"] = systemInfo[@BSG_KSSystemField_SystemName];
Expand Down
215 changes: 215 additions & 0 deletions Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_Jailbreak.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
//
// bsg_jailbreak.h
// Bugsnag
//
// Created by Karl Stenerud on 10.02.21.
// Copyright © 2021 Bugsnag Inc. All rights reserved.
//
//
// Robust Enough Jailbreak Detection
// ---------------------------------
//
// Perfect jailbreak detection, like perfect copy protection, is a fool's errand.
// But perfection isn't necessary for our purposes. We just need to make it tricky enough
// that only a complicated per-app targeted tweak would work. Once an app gets popular
// enough to warrant the time and effort of a targeted tweak, they'll need custom
// jailbreak detection code anyway for the inevitable cat-and-mouse game.
//
// This code operates on the following anti-anti-jailbreak-detection principles:
//
// - Functions can be patched by a general tweak, but syscalls cannot.
// - "environ" is a global variable, which cannot easily be pre-manipulated without
// potential breakage elsewhere.
//
// We check the following things:
//
// - Can we create a file in /tmp? (/tmp has perms 777, but sandboxed apps can't see it)
// - Does Cydia's MobileSubstrate library exist? (used for tweaks and cracks)
// - Does /etc/apt exist? (Debian's apt is used for non-app-store app distribution)
// - Does the ENV contain an "insert libraries" directive? (used to override functions)
//
// To guard against function overrides, we use syscalls for some of the checks, with a
// graceful fallback to libc functions if we're on an unknown architecture. We also
// stick to very basic and old syscalls that have remained stable for decades.
//

#ifndef bsg_jailbreak_h
#define bsg_jailbreak_h

#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <TargetConditionals.h>

// The global environ variable must be imported this way.
// See: https://opensource.apple.com/source/Libc/Libc-1439.40.11/man/FreeBSD/environ.7
extern char **environ;

static inline bool bsg_local_is_insert_libraries_env_var(const char* str) {
// DYLD_INSERT_LIBRARIES lets you override functions by loading other libraries first.
// This is a common technique used for defeating detection.
// See: https://opensource.apple.com/source/dyld/dyld-832.7.3/doc/man/man1/dyld.1
const char insert[] = "DYLD_INSERT_LIBRARIES";
return __builtin_memcmp(str, insert, sizeof(insert)) == 0;
}

// Note: The following are written as macros to force them always inline so that there
// are no function calls (which could be patched out). There's enough repetition
// going on that the compiler might refuse to inline if these were functions.

// Reminder: Always follow proper macro hygene to prevent unwanted side effects:
// - Surround macros in a do { ... } while(0) construct to prevent leakage.
// - Surround parameters in parentheses when accessing them.
// - Do not access a parameter more than once.
// - Make local copies of input parameters to help enforce their types.
// - Use pointers for output parameters, with labels that identify them as such.
// - Beware of global consts or defines bleeding through.

#if TARGET_CPU_ARM64
#define BSG_HAS_CUSTOM_SYSCALL 1

// ARM64 3-parameter syscall
// Writes -1 to *(pResult) on failure, or the actual result on success.
//
// Implementation Details:
// - Syscall# is in x16, params in x0, x1, x2, and return in x0.
// - Carry bit is cleared on success, set on failure (we copy the carry bit to x3).
// - We must also inform the compiler that memory and condition codes may get clobbered.
#define bsg_syscall3(call_num, param0, param1, param2, pResult) do { \
register uintptr_t call asm("x16") = (uintptr_t)(call_num); \
register uintptr_t p0 asm("x0") = (uintptr_t)(param0); \
register uintptr_t p1 asm("x1") = (uintptr_t)(param1); \
register uintptr_t p2 asm("x2") = (uintptr_t)(param2); \
register uintptr_t carry_bit asm("x3") = 0; \
asm volatile("svc #0x80\n" \
"mov x3, #0\n" \
"adc x3, x3, x3\n" \
: "=r"(carry_bit), "=r"(p0),"=r"(p1) \
: "r"(p0), "r"(p1), "r"(p2), "r"(call), "r"(carry_bit) \
: "memory", "cc"); \
if(carry_bit == 1) { \
*(pResult) = -1; \
} else {\
*(pResult) = (int)p0; \
} \
} while(0)

#elif TARGET_CPU_X86_64
#define BSG_HAS_CUSTOM_SYSCALL 1

// X86_64 3-parameter syscall
// Writes -1 to *(pResult) on failure, or the actual result on success.
//
// Implementation Details:
// - Syscall# is in RAX, params in RDI, RSI, RDX, and return in RAX.
// - Carry bit is cleared on success, set on failure.
// - We must also inform the compiler that memory, rcx, r11 may get clobbered.
#define bsg_syscall3(call_num, param0, param1, param2, pResult) do { \
register uintptr_t rax = (uintptr_t)(call_num) | (2<<24); \
register uintptr_t p0 = (uintptr_t)(param0); \
register uintptr_t p1 = (uintptr_t)(param1); \
register uintptr_t p2 = (uintptr_t)(param2); \
register uintptr_t carry_bit = 0; \
asm volatile( \
"syscall" \
: "=@ccc"(carry_bit), "+a"(rax) \
: "D" (p0), "S" (p1), "d" (p2) \
: "memory", "rcx", "r11"); \
if(carry_bit == 1) { \
*(pResult) = -1; \
} else { \
*(pResult) = (int)rax; \
} \
} while(0)

#else
#define BSG_HAS_CUSTOM_SYSCALL 0

// Unhandled architecture.
// We fall back to the libc functions in this case, mimicing the syscall-like API.
// See definitions below.

#endif /* TARGET_CPU_XYZ */


#if BSG_HAS_CUSTOM_SYSCALL

// See: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/kern/syscalls.master
#define BSG_SYSCALL_OPEN 5
#define bsg_syscall_open(path, flags, mode, pResult) bsg_syscall3(BSG_SYSCALL_OPEN, (uintptr_t)path, flags, mode, pResult)

#else

#define bsg_syscall_open(path, flags, mode, pResult) do {*(pResult) = open(path, flags, mode);} while(0)

#endif /* BSG_HAS_CUSTOM_SYSCALL */


/**
* Get this device's jailbreak status.
* Stores nonzero in *(pIsJailbroken) if the device is jailbroken, 0 otherwise.
* Note: Implemented as a macro to force it inline always.
*/
#if !TARGET_OS_SIMULATOR && !TARGET_OS_OSX
#define get_jailbreak_status(pIsJailbroken) do { \
int fd = 0; \
\
bool tmp_file_is_accessible = false; \
bool mobile_substrate_exists = false; \
bool etc_apt_exists = false; \
bool has_insert_libraries = false; \
\
const char* test_write_file = "/tmp/bugsnag-check.txt"; \
remove(test_write_file); \
bsg_syscall_open(test_write_file, O_CREAT, 0644, &fd); \
if(fd > 0) { \
close(fd); \
tmp_file_is_accessible = true; \
} else { \
bsg_syscall_open(test_write_file, O_RDONLY, 0, &fd); \
if(fd > 0) { \
close(fd); \
tmp_file_is_accessible = true; \
} \
} \
remove(test_write_file); \
\
const char* mobile_substrate_path = "/Library/MobileSubstrate/MobileSubstrate.dylib"; \
bsg_syscall_open(mobile_substrate_path, O_RDONLY, 0, &fd); \
if(fd > 0) { \
close(fd); \
mobile_substrate_exists = true; \
} \
\
const char* etc_apt_path = "/etc/apt"; \
struct stat st; \
if(stat(etc_apt_path, &st) == 0) { \
etc_apt_exists = true; \
} \
\
for(int i = 0; environ[i] != NULL; i++) { \
if(bsg_local_is_insert_libraries_env_var(environ[i])) { \
has_insert_libraries = true; \
break; \
} \
} \
\
*(pIsJailbroken) = tmp_file_is_accessible || \
mobile_substrate_exists || \
etc_apt_exists || \
has_insert_libraries; \
} while(0)

#else

// "/tmp" is accessible on the simulator, which makes the JB test come back positive, so
// report false on the simulator.
#define get_jailbreak_status(pIsJailbroken) do { *(pIsJailbroken) = 0; } while(0)

#endif /* !TARGET_OS_SIMULATOR */


#endif /* bsg_jailbreak_h */
27 changes: 24 additions & 3 deletions Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@
#if BSG_PLATFORM_IOS || BSG_PLATFORM_TVOS
#import "BSGUIKit.h"
#endif
#import "BSG_Jailbreak.h"


static inline bool is_jailbroken() {
static bool initialized_jb;
static bool is_jb;
if(!initialized_jb) {
get_jailbreak_status(&is_jb);

// Also keep using the old detection method.
if(bsg_mach_headers_image_named("MobileSubstrate", false) != NULL) {
is_jb = true;
}
initialized_jb = true;
}

return is_jb;
}

@implementation BSG_KSSystemInfo

Expand Down Expand Up @@ -261,7 +279,7 @@ + (NSString *)currentCPUArch {
* @return YES if the device is jailbroken.
*/
+ (BOOL)isJailbroken {
return bsg_mach_headers_image_named("MobileSubstrate", false) != NULL;
return is_jailbroken();
}

/** Check if the current build is a debug build.
Expand Down Expand Up @@ -494,8 +512,11 @@ + (BOOL)isInForeground:(UIApplicationState)state {

const char *bsg_kssysteminfo_toJSON(void) {
NSError *error;
NSDictionary *systemInfo = [NSMutableDictionary
dictionaryWithDictionary:[BSG_KSSystemInfo systemInfo]];
NSMutableDictionary *systemInfo = [[BSG_KSSystemInfo systemInfo] mutableCopy];

// Make sure the jailbroken status didn't get patched out.
systemInfo[@BSG_KSSystemField_Jailbroken] = @(is_jailbroken());

NSMutableData *jsonData =
(NSMutableData *)[BSG_KSJSONCodec encode:systemInfo
options:BSG_KSJSONEncodeOptionSorted
Expand Down
1 change: 1 addition & 0 deletions Bugsnag/Payload/BugsnagDeviceWithState.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ + (BugsnagDeviceWithState *) deviceFromJson:(NSDictionary *)json {
+ (BugsnagDeviceWithState *)deviceWithOomData:(NSDictionary *)data {
BugsnagDeviceWithState *device = [BugsnagDeviceWithState new];
device.id = data[@"id"];
device.jailbroken = [data[@"jailbroken"] boolValue];
device.osVersion = data[@"osVersion"];
device.osName = data[@"osName"];
device.manufacturer = @"Apple";
Expand Down
40 changes: 24 additions & 16 deletions Bugsnag/Payload/BugsnagError.m
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,25 @@ + (BugsnagError *)errorFromJson:(NSDictionary *)json {
}

- (void)updateWithCrashInfoMessage:(NSString *)crashInfoMessage {
@try {
// Messages that match this pattern should override the errorClass (and errorMessage if there is enough information.)
NSString *pattern = @"^(Assertion failed|Fatal error|Precondition failed): ((.+): )?file .+, line \\d+\n$";
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
NSArray<NSTextCheckingResult *> *matches = [regex matchesInString:crashInfoMessage options:0 range:NSMakeRange(0, crashInfoMessage.length)];
NSArray<NSString *> *patterns = @[
// From Swift 2.2: https://github.com/apple/swift/blob/swift-2.2-RELEASE/stdlib/public/stubs/Assert.cpp#L24-L39
@"^(assertion failed|fatal error|precondition failed): ((.+): )?file .+, line \\d+\n$",
// From Swift 4.1: https://github.com/apple/swift/commit/d03a575279cf5c523779ef68f8d7903f09ba901e
@"^(Assertion failed|Fatal error|Precondition failed): ((.+): )?file .+, line \\d+\n$",
// From Swift 5.4: https://github.com/apple/swift/commit/1a051719e3b1b7c37a856684dd037d482fef8e59
@"^.+:\\d+: (Assertion failed|Fatal error|Precondition failed)(: (.+))?\n$",
];

for (NSString *pattern in patterns) {
NSArray<NSTextCheckingResult *> *matches = nil;
@try {
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:nil];
matches = [regex matchesInString:crashInfoMessage options:0 range:NSMakeRange(0, crashInfoMessage.length)];
} @catch (NSException *exception) {
bsg_log_err(@"Exception thrown while parsing crash info message: %@", exception);
}
if (matches.count != 1 || matches[0].numberOfRanges != 4) {
if (!self.errorMessage.length) {
// It's better to fall back to the raw string than have an empty errorMessage.
self.errorMessage = crashInfoMessage;
}
return;
continue;
}
NSRange errorClassRange = [matches[0] rangeAtIndex:1];
if (errorClassRange.location != NSNotFound) {
Expand All @@ -147,12 +155,12 @@ - (void)updateWithCrashInfoMessage:(NSString *)crashInfoMessage {
if (errorMessageRange.location != NSNotFound) {
self.errorMessage = [crashInfoMessage substringWithRange:errorMessageRange];
}
} @catch (NSException *exception) {
bsg_log_err(@"Exception thrown while parsing crash info message: %@", exception);
if (!self.errorMessage.length) {
// It's better to fall back to the raw string than have an empty errorMessage.
self.errorMessage = crashInfoMessage;
}
return; //!OCLint
}

if (!self.errorMessage.length) {
// It's better to fall back to the raw string than have an empty errorMessage.
self.errorMessage = crashInfoMessage;
}
}

Expand Down
2 changes: 1 addition & 1 deletion Bugsnag/Payload/BugsnagNotifier.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ - (instancetype)init {
#else
self.name = @"Bugsnag Objective-C";
#endif
self.version = @"6.6.3";
self.version = @"6.6.4";
self.url = @"https://github.com/bugsnag/bugsnag-cocoa";
self.dependencies = [NSMutableArray new];
}
Expand Down
Loading

0 comments on commit 6476eb4

Please sign in to comment.