Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSMutableDictionary inside JSONModel - EXC_BAD_ACCESS KERN_INVALID_ADDRESS

Crashlytics reported this crash in one of my apps and I am not able to reproduce it at all, no matter what I do. This happens to about 5% of the users, so it's a pretty big deal. I'm posting screenshots with the crash report and also the methods that are mentioned in the crash report. Any idea how to solve this?

Crash Report

This is where the app crashed:

#pragma mark - custom transformations
-(BOOL)__customSetValue:(id<NSObject>)value forProperty:(JSONModelClassProperty*)property
{
    if (!property.customSetters)
        property.customSetters = [NSMutableDictionary new];

    NSString *className = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:[value class]]);

    if (!property.customSetters[className]) {
        //check for a custom property setter method
        NSString* ucfirstName = [property.name stringByReplacingCharactersInRange:NSMakeRange(0,1)
                                                                       withString:[[property.name substringToIndex:1] uppercaseString]];
        NSString* selectorName = [NSString stringWithFormat:@"set%@With%@:", ucfirstName, className];

        SEL customPropertySetter = NSSelectorFromString(selectorName);

        //check if there's a custom selector like this
        if (![self respondsToSelector: customPropertySetter]) {
            property.customSetters[className] = [NSNull null]; // this is line 855
            return NO;
        }

        //cache the custom setter selector
        property.customSetters[className] = selectorName;
    }

    if (property.customSetters[className] != [NSNull null]) {
        //call the custom setter
        //https://github.com/steipete
        SEL selector = NSSelectorFromString(property.customSetters[className]);
        ((void (*) (id, SEL, id))objc_msgSend)(self, selector, value);
        return YES;
    }

    return NO;
}

This is the originating method:

-(void)reloadUserInfoWithCompletion:(void (^) (LoginObject *response))handler andFailure:(void (^)(NSError *err))failureHandler {
    NSString *lat;
    NSString *lon;

    lat = [NSString stringWithFormat:@"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.latitude];
    lon = [NSString stringWithFormat:@"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.longitude];

    NSMutableDictionary *params = [NSMutableDictionary new];
    [params setObject:lat forKey:@"latitude"];
    [params setObject:lon forKey:@"longitude"];

    [[LoginHandler sharedInstance] getLoginToken:^(NSString *response) {

        NSDictionary *headers;
        if (response) {
            headers = @{@"Login-Token":response};
        }
        GETRequest *req = [GETRequest new];
        [req setCompletionHandler:^(NSString *response) {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                NSLog(@"response: %@",response);
                NSError *err = nil;
                self.loginObject.userDetails = [[User alloc] initWithString:response error:&err]; // <- this is the line reported in the crash
                [self storeLoginObject];
                NSLog(@"%@",self.loginObject.userDetails);
//                [Utils updateFiltersFullAccessIfAll]; 
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (handler) {
                        handler(self.loginObject);
                    }
                });
            });
        }];
        [req setFailedHandler:^(NSError *err) {
            if (failureHandler) {
                failureHandler(err);
            }
        }];
        NSLog(@"%@",params);
        [req requestWithLinkString:USER_DETAILS parameters:nil andHeaders:headers];
    }];

}
like image 283
stonycis Avatar asked Jan 26 '16 11:01

stonycis


1 Answers

So setObject:forKey: can cause problems in two ways. 1. If object is nil or 2. the key is nil. Both could cause the crash you are seeing. Given that you are setting the object to [NSNull null] it is probably safe to assume that it is the key giving you problems (on line 855).

Walking back from there that would reveal that className is nil. If you look, your code does not protect against this. You make an assumption here that NSStringFromClass (a couple lines before) is giving you back a valid string, which assumes that the value originally passed into the method is non-nil. If it is nil it would make it past all of your checks, including !property.customSetters[className], since this would be !nil allowing it to enter the if.

If I am reading your code right (a bit hard since I cannot test any of my assumptions) NSLog(@"response: %@",response); would print out a nil response.

Try seeing how your code handles these unexpected nils and let me know in the comments how things go.

like image 98
Firo Avatar answered Nov 06 '22 02:11

Firo