CoreML model decryption fails after repeatedly loading the same model (iOS)

MLModel decryption fails after the same model is loaded some variable amount of times. Here is the error message:

2022-06-08 11:26:19.310357-0400 decrypt-bug-repro[312:5628] [coreml] Could not create decrypt session for /private/var/containers/Bundle/Application/684BF7E4-8573-49A2-9D26-02D2E4D9C4E2/decrypt-bug-repro.app/model.mlmodelc : error=Error Domain=com.apple.CoreML Code=9 "Failed to set up decrypt context for /private/var/containers/Bundle/Application/684BF7E4-8573-49A2-9D26-02D2E4D9C4E2/decrypt-bug-repro.app/model.mlmodelc. error:-42905" UserInfo={NSLocalizedDescription=Failed to set up decrypt context for /private/var/containers/Bundle/Application/684BF7E4-8573-49A2-9D26-02D2E4D9C4E2/decrypt-bug-repro.app/model.mlmodelc. error:-42905}

The number of successful loads before encountering the error seems to vary depending on how recently the iOS device was rebooted. Just after rebooting the device, I am able to load the model exactly 99 times before failing on the 100th attempt. Subsequent attempts to run my test app results in a progressively decreasing number of successful loads before failing. I have seen the number of successful loads go as low as one.

I am able to consistently reproduce this issue with the following sample code:

#include <CoreML/CoreML.h>
#include <Foundation/Foundation.h>

#include <atomic>
#include <csignal>

int main()
{
    static std::atomic_bool keepRunning = true;

    std::signal(SIGINT, +[](int) { keepRunning = false; });

    @autoreleasepool {
        NSBundle* bundle = [NSBundle mainBundle];
        NSString* bundlePath = [bundle bundlePath];
        NSString* modelPath = [NSString stringWithFormat:@"%@/%@", bundlePath, @"model.mlmodelc"];

        NSURL* modelURL = [NSURL fileURLWithPath:modelPath];

        int numSuccessfulLoads = 0;

        while (keepRunning)
        {
            NSError* error = nil;
            MLModel* model = [MLModel modelWithContentsOfURL:modelURL error:&error];

            if (error != nil)
                @throw [NSException exceptionWithName:@"Failed to load model" reason:[error localizedDescription] userInfo:nil];

            if (model == nil)
                @throw [NSException exceptionWithName:@"Model is nil" reason:@"Failed to init model" userInfo:nil];                  

            NSLog(@"Successfully loaded model (%d)", ++numSuccessfulLoads);
        }
    }
}

What is the cause of this issue? How can we prevent it from happening?

Answered by Frameworks Engineer in 716361022

This is expected. We limit the number of active decrypt sessions for every app. The issue is that your model is being retained in the autorelease pool and that is holding on to an active decrypt session. If you move the @autoreleasepool { to after while (keepRunning), you should be able to resolve this issue.

Accepted Answer

This is expected. We limit the number of active decrypt sessions for every app. The issue is that your model is being retained in the autorelease pool and that is holding on to an active decrypt session. If you move the @autoreleasepool { to after while (keepRunning), you should be able to resolve this issue.

Your suggested fix does indeed resolve the issue. Thank you!

CoreML model decryption fails after repeatedly loading the same model (iOS)
 
 
Q