NSFetchedResultsController use-after-free issues in iOS 16.1

In iOS 16.1 there are two memory-access issues that have surfaced in related to NSFetchedResultsController:

  1. If your sectionNameKeyPath returns an ordering that doesn't match that of the fetch request's first sort descriptor, the internal NSError instance created to warn you of this fact gets over-released and may crash the application.
  2. If your implementation of sectionNameKeyPath is a computed property implemented in objective-c, the fetched results controller may crash due to over-releasing the returned strings (it's possible this may also happen in Swift, though I was unable to produce the behavior).

I've filed two feedbacks (FB11652942 & FB11653996) regarding these issues, but also wanted to raise them here in case that may help expedite their resolution.

The offending method seems to be -[NSFetchedResultsController _computeSectionInfo:error:], which, from inspecting the disassembly, appears to now contain some references to objc_autoreleasePoolPop which were previously absent.

Regarding issue 2. specifically – it's unclear what the ownership model for the value returned from sectionNameKeyPath is intended to be if said key path produces a computed value. Does the fetched results controller take ownership of it, or does the framework assume that the corresponding managed object is the owner?

-Jamie

Post not yet marked as solved Up vote post of jamie_sq Down vote post of jamie_sq
2k views

Replies

Thanks Jamie, that saved me a lot of research. However, I am wondering how you found these kind of bugs in the iOS (over-releasing the returned strings)?

  • We had crash reports that were only occurring on the iOS 16.1 betas that pointed in the direction of this issue. Once a reproducible case was found, running with NSZombie detection enabled (edit scheme > diagnostics > zombie objects) revealed that there was a use-after-free bug.

Add a Comment

Hello Jaime, thanks for reporting these issues to Apple. Unfortunately it looks like this crashes still present in released iOS version. Have you found any workarounds for these issues?

  • The solution to 1. is to avoid returning section names in an order that does not match the sort descriptor's ordering. The docs explicitly state that you must do this, though the resulting memory corruption seems like a bug. I'm not certain of a general solution to 2., but you might try adding an additional copy call to the strings returned from sectionNameKeyPath, or converting the implementation to Swift. Both of these seemed to work in the sample project I submitted to Apple.

  • Thanks, used approach with rewrite ins Swift. Looks more convenient because additional copies will create some memory leaks on previous iOS versions.

Add a Comment

Yup! I am suddenly seeing a lot of crash reports (over 100 in the last couple of days) and ALL from users on iOS 16.1. This is new and nothing in that part of the App changed. Crashing right after the CoreData is initialized and NSPersistentCloudKitContainer is ready. At that point the controller does a fetchRequest for items in the Table and Crash with some kind of memory error.

In my case it is simply making a request for items sorted alphabetically?!

I filed a bug report on this with all the crash logs we are getting in Organizer (FB11875507). And then submitted a TSR for help with a work around. Apples response was that there is no work around and it is there issue. Seeing a lot less crash reports (but still some with the same issue) on iOS 16.2 and now also some from beta users on 16.3, but the numbers are way down and I have been telling customers to upgrade to latest iOS. It helps most.

  • Geeze Apple get this SORTED ASAP!!! The Crash Reports are piling up and I am getting a lot of ticked off customers giving negative reviews! Put out a dot release that fixes this ASAP please.

Add a Comment

Issue STILL there in 16.3. APPLE!!!! Get the finger out on this one! This is causing hundreds of crashes and I get support emails every day about this crap. Also related are when the user uses a fetch with predicate and if the initial poster is correct will definitely change the section naming order. :(

Issue STILL there in 16.3.1?! Does anyone have anymore information on this and how to resolve it or a workaround?! This is starting to be a real issue for me with bad reviews.

@jamie_sq or anyone have any thoughts regarding this issue related to NSFetch and sectionHeaders?

The code in question has worked for years but started failing on iOS 16.

After seeing this post and realizing it had something to do with section ordering and seeing that it seems to only happen on devices using foreign languages we looked more carefully at our code and realized that the nameFirstLetter method of our NSManagedObject which is the ONLY Entity in our CoreData database and which returns the string used for sectionNameKeyPath was not properly handling Unicode characters.

nameFirstLetter method simply grabs the first character of NAME field on the NSManagedObject. Name is an actual stored field and this method takes the first character and returns it. We improved this to use rangeOfComposedCharacterSequenceAtIndex. That works ok until we tried to do [str localizedUppercaseString] on it. And NOW we see exactly the crash our customers are seeing.

-(NSString *)nameFirstLetter
{
    if(self.name != nil) {
        return [self.name substringWithRange:[self.name rangeOfComposedCharacterSequenceAtIndex:0]];

        /* The above works with the issue that sections show up for lower case letters and then again for uppercase.
         As soon as we do the following the NSFetchResultController CRASHES with the _computeSectionInfo error */

        return [[self.name substringWithRange:[self.name rangeOfComposedCharacterSequenceAtIndex:0]] localizedUppercaseString];
    }
    else
        return @"?";
}

We don't understand why this should be a problem given that the NSFetchResultController is initialized to do localizedCaseInsensitiveCompare? This is how we set up the FetchControllers sorts.

-(void)initializeFetchedResultsController
{
    // Already inititialized
    if(_fetchedResultsController != nil) return;
    
    // Create and configure a fetch request for Charts
    NSManagedObjectContext *moc = ((iPhemerisAppDelegate *)[[UIApplication sharedApplication] delegate]).coreDataUtil.pc.viewContext;
    if(!moc) {
        NSLog(@"*** SavedChartView MOC NOT READY");
        return;
    }    
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Charts" inManagedObjectContext:moc];
    [fetchRequest setEntity:entity];
    
    // Create the sort descriptors array
    NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
    NSSortDescriptor *chartTypeDescriptor = [[NSSortDescriptor alloc] initWithKey:@"chartType" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:nameDescriptor, chartTypeDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];
    
    // Create and initialize the fetch results controller.
    _sectionNameKeyPath = @"nameFirstLetter";
    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc sectionNameKeyPath:_sectionNameKeyPath cacheName:nil];
    _fetchedResultsController.delegate = self;
}
  • @cliffr one of the crashing instances we observed appeared to be due to the use of uppercaseStringWithLocale when returning the section header. The workarounds we found were to either rewrite that code in Swift or to copy the uppercased string before returning it. Assuming that you're using ARC, copying will also autorelease, which appears to be sufficient to make the strings live through whatever internal loop in CoreData that isn't explicitly retaining them long enough.

Add a Comment