Security SecTrustRef Memory Leak

I am trying to evaluate a server trust, and my code is leaking memory on line 06. This function takes in a serverTrust object, evaluates it, and passes the result on as a BOOL. I have tried updating to the latest API (SecTrustEvaluateWithError) that takes in a SecTrustRef and an NSError address, yet I still experience memory leaks within that function. I presume that it has something to do with the SecTrustRef object that I am passing in, but I can't seem to understand what is happening. Any ideas?


static BOOL SVCServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;

     OSStatus status = SecTrustEvaluate(serverTrust, &result);
     __Require_noErr_Quiet(status, _out);
     isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
_out:
     return isValid;

Replies

First up, temporarily change your

SVCServerTrustIsValid
function to just return
YES
. Do you still see the memory leak?

If not, the problem is clearly related to

SecTrustEvaluate
because nothing else in
SVCServerTrustIsValid
even allocates memory. In that case I recommend that you file a bug report about the leak. Please post your bug number, just for the record.

As to workarounds, the first thing to do is assess whether it’s worth worrying about this. How much memory is this leaking? And how often is it called?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hey Quinn, thank you for your response.
When I changed SVCServerTrustIsValid to return YES, I noticed that the leak was not present any more. I did, however, notice that another leak appeared somewhere else.


static NSArray * SVCPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
    SecPolicyRef policy = SecPolicyCreateBasicX509();
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);


In the code above, with SVCServerTrustIsValid returning YES, I see a leak on line 03. Could it have something to do with my SecTrustRef object?
The amount of memory leaking usually hovers around 100 KiB initially in `SVCServerTrustIsValid`, yet it increases by about 8-15 KiB with every request that I make.

In the code above, with

SVCServerTrustIsValid
returning
YES
, I see a leak on line 03. Could it have something to do with my
SecTrustRef
object?

It’s hard to say without more details on the leak. Do you get any type information about the leaked objects.

Still, the most obvious cause for potential leaks in the code you posted is line 2, where you must take responsibility for releasing

policy
under Core Foundations create/copy rule.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

More information on the leak:

  • Leaked Object: SecCertificate
  • Responsible Library: Security
  • Responsible Frame: SecCertificateCreateWithBytes


Later on in my code, I already release `policy`, so I don't believe that is the culprit.

Later on in my code, I already release

policy
, so I don't believe that is the culprit.

Fair enough. I tried reproducing this with a small test project and it doesn’t leak for me. Pasted in below is the relevant code from my test project. Lines 39 through 42 are my attempts to reproduce what you’re doing with your code.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
@interface MainViewController () <NSURLSessionDelegate>

@property (nonatomic, strong, readwrite) NSURLSession * session;

@end

@implementation MainViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    #pragma unused(tableView)
    #pragma unused(indexPath)

    if (self.session == nil) {
        NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
        self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    NSURL * url = [NSURL URLWithString:@"https://example.com"];
    NSURLRequest * request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0];
    [[self.session dataTaskWithRequest:request completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) {
        #pragma unused(data)
        #pragma unused(response)
        #pragma unused(error)
        NSLog(@"task is done");
    }] resume];
    NSLog(@"task did start");

    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    #pragma unused(session)
    NSString * authenticationMethod = challenge.protectionSpace.authenticationMethod;
    NSLog(@"challenge %@", authenticationMethod);
    if ([authenticationMethod isEqual:NSURLAuthenticationMethodServerTrust]) {
        SecTrustRef trust;

        trust = challenge.protectionSpace.serverTrust;

        SecPolicyRef policy = SecPolicyCreateBasicX509();
        CFIndex certificateCount = SecTrustGetCertificateCount(trust);
        NSLog(@"certificate count: %zd", certificateCount);
        CFRelease(policy);

        NSURLCredential * credential = [NSURLCredential credentialForTrust:trust];
        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
    } else {
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
    }
}

@end