Skip to content

Commit

Permalink
fix: Crash when Getting Subclasses (#1396)
Browse files Browse the repository at this point in the history
Calling class_getSuperclass sometimes leads to EXC_I386_GPFLT. OneSignal
had the same issue OneSignal/OneSignal-iOS-SDK#278
and it was fixed with OneSignal/OneSignal-iOS-SDK#301.
This PR applies the changes they did to our code to fix the problem.

Fixes GH-1395
  • Loading branch information
philipphofmann authored Oct 18, 2021
1 parent e3ae241 commit 2931d32
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- fix: Crash when Getting Subclasses (#1396)

## 7.4.5

- fix: Remove Check for Original Method Call When Swizzling (#1383)
Expand Down
37 changes: 31 additions & 6 deletions Sources/Sentry/SentryUIViewControllerSwizziling.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,24 +68,49 @@ - (void)swizzleSubclassesOf:(Class)parentClass
{
[dispatchQueue dispatchAsyncWithBlock:^{
int numClasses = objc_getClassList(NULL, 0);
Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);

if (numClasses <= 0) {
[SentryLog logWithMessage:@"No classes found when retrieving class list."
andLevel:kSentryLevelError];
NSString *msg =
[NSString stringWithFormat:@"No classes found when retrieving class list for %@.",
parentClass];
[SentryLog logWithMessage:msg andLevel:kSentryLevelError];
return;
}

int memSize = sizeof(Class) * numClasses;
Class *classes = (__unsafe_unretained Class *)malloc(memSize);

if (classes == NULL && memSize) {
NSString *msg = [NSString
stringWithFormat:@"Couldn't allocate memory for retrieving class list for %@",
parentClass];
[SentryLog logWithMessage:msg andLevel:kSentryLevelError];
return;
}

numClasses = objc_getClassList(classes, numClasses);

// Storing the actual classes in an NSArray would call initialize of the class, which we
// must avoid as we are on a background thread here and dealing with UIViewControllers,
// which assume they are running on the main thread. Therefore, we store the indexes instead
// so we can search for the subclasses on a background thread.
NSMutableArray *indexesToSwizzle = [NSMutableArray new];
for (NSInteger i = 0; i < numClasses; i++) {
Class superClass = classes[i];
do {

// Don't add the parent class to list of sublcasses
if (superClass == parentClass) {
continue;
}

// Using a do while loop, like pointed out in Cocoa with Love
// (https://www.cocoawithlove.com/2010/01/getting-subclasses-of-objective-c-class.html)
// can lead to EXC_I386_GPFLT which, stands for General Protection Fault and means we
// are doing something we shouldn't do. It's safer to use a regular while loop to check
// if superClass is valid.
while (superClass && superClass != parentClass) {
superClass = class_getSuperclass(superClass);
} while (superClass && superClass != parentClass);
}

if (superClass != nil) {
[indexesToSwizzle addObject:@(i)];
Expand Down

0 comments on commit 2931d32

Please sign in to comment.