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

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)?

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?

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.

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;
}
NSFetchedResultsController use-after-free issues in iOS 16.1
 
 
Q