-
-
Save steipete/08974d7544d328fb7978c10505834e00 to your computer and use it in GitHub Desktop.
static void PSPDFFixiOS12AccessibilityTestCrash(void) { | |
let accessibilityLoaderClass = NSClassFromString(@"UIAccessibilityInformationLoader"); | |
let accessibilitySEL = NSSelectorFromString(@"_loadAccessibilityInformationOnMainThread:"); | |
__block IMP originalIMP = pspdf_swizzleSelectorWithBlock(accessibilityLoaderClass, accessibilitySEL, ^(id _self, BOOL onMainThread) { | |
@try { | |
((void (*)(id, SEL, BOOL))originalIMP)(_self, accessibilitySEL, onMainThread); | |
} @catch (NSException *exception) { | |
NSLog(@"Exception received: %@", exception); | |
if ([exception.name isEqualToString:NSInvalidArgumentException] && [exception.reason containsString:@"_accessibilityLoadAccessibilityInformation"]) { | |
NSLog(@"Ignoring IOS 12b5 weirdness..."); | |
} else { | |
// Rethrow other things | |
@throw exception; | |
} | |
} | |
}); | |
} | |
// If you don't yet have such swizzling helpers, here's ours: | |
// Extracted straight from the ObjC Runtime headers: | |
// https://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc-abi.h | |
// | |
// objc_msgSendSuper2() takes the current search class, not its superclass. | |
OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...) | |
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); | |
OBJC_EXPORT void objc_msgSendSuper2_stret(struct objc_super *super, SEL op,...) | |
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); | |
// http://defagos.github.io/yet_another_article_about_method_swizzling/ | |
// Returns the original implementation | |
PSPDF_EXTERN _Nullable IMP pspdf_swizzleSelector(Class clazz, SEL selector, IMP newImplementation) { | |
NSCParameterAssert(clazz); | |
NSCParameterAssert(selector); | |
NSCParameterAssert(newImplementation); | |
// If the method does not exist for this class, do nothing. | |
const Method method = class_getInstanceMethod(clazz, selector); | |
if (!method) { | |
PSPDFLogError(@"%@ doesn't exist in %@.", NSStringFromSelector(selector), NSStringFromClass(clazz)); | |
// Cannot swizzle methods which are not implemented by the class or one of its parents. | |
return NULL; | |
} | |
// Make sure the class implements the method. If this is not the case, inject an implementation, only calling 'super'. | |
const char *types = method_getTypeEncoding(method); | |
@synchronized(clazz) { | |
// class_addMethod will simply return NO if the method is already implemented. | |
#if !defined(__arm64__) | |
// Sufficiently large struct | |
typedef struct LargeStruct_ { char dummy[16]; } LargeStruct; | |
NSUInteger retSize = 0; | |
NSGetSizeAndAlignment(types, &retSize, NULL); | |
// Large structs on 32-bit architectures | |
// TODO: This is incorrect for some structs on some architectures. Needs to be hardcoded, this cannot be safely inferred at runtime. | |
// https://twitter.com/gparker/status/1028564412339113984 | |
if (sizeof(void *) == 4 && types[0] == _C_STRUCT_B && retSize != 1 && retSize != 2 && retSize != 4 && retSize != 8) { | |
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { | |
struct objc_super super = {self, clazz}; | |
return ((LargeStruct(*)(struct objc_super *, SEL, va_list))objc_msgSendSuper2_stret)(&super, selector, argp); | |
}), types); | |
} | |
// All other cases | |
else { | |
#endif | |
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { | |
struct objc_super super = {self, clazz}; | |
return ((id(*)(struct objc_super *, SEL, va_list))objc_msgSendSuper2)(&super, selector, argp); | |
}), types); | |
#if !defined(__arm64__) | |
} | |
#endif | |
// Swizzling | |
return class_replaceMethod(clazz, selector, newImplementation, types); | |
} | |
} | |
PSPDF_EXTERN _Nullable IMP pspdf_swizzleSelectorWithBlock(Class clazz, SEL selector, id newImplementationBlock) { | |
const IMP newImplementation = imp_implementationWithBlock(newImplementationBlock); | |
return pspdf_swizzleSelector(clazz, selector, newImplementation); | |
} |
Yes, we fixed the bug by fixing our proxy forwarder logic. The 3-step process from respondsToSelector, methodSignature and forwardInvocation needs to be consistent for any call - even unexpected ones. We use a pre-built cache to speed up lookups and didn't consider the case where unexpected calls might be sent - especially for custom Apple-internal object categories that might skip the respondsToSelector part.
@steipete I'm getting this error in a production app using PSPDFKit 7.6.2. Was this fixed on a later release? Thanks.
This has been fixed in PSPDFKit 7.7.2 for iOS.
Here's the detailed changelog: https://pspdfkit.com/changelog/ios/#7.7.2
See: Fixes an issue where iOS 12 could throw an exception inside -[UIAccessibilityInformationLoader _loadAccessibilityInformationOnMainThread:]. (#16489)
Do you mean that you have fixed the bug without hook the method? Can you tell me how to?
Thanks a lot!