-
-
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); | |
} |
Update: This was our bug. We use a multicast delegate internally, which missed to generate the correct method signature for a category loaded via UIAccessibility
onto NSObject
. This was fixed by forwarding to the delegate's implementation if our protocol cache missed the entry.
Update: This was our bug. We use a multicast delegate internally, which missed to generate the correct method signature for a category loaded via
UIAccessibility
ontoNSObject
. This was fixed by forwarding to the delegate's implementation if our protocol cache missed the entry.
Do you mean that you have fixed the bug without hook the method? Can you tell me how to?
Thanks a lot!
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)
Filed as rdar://43228744 http://openradar.appspot.com/43228744