NSGenericException "Start date cannot be later in time than end date!" in -[NSURLSessionTaskMetrics _initWithTask:]

Originator:nobrien
Number:rdar://28301343 Date Originated:14-Sep-2016 09:16 AM
Status:Open Resolved:
Product:iOS SDK Product Version:10
Classification:Crash/Hang/Data Loss Reproducible:Unable
 
Summary:
Trickling in crashes with NSGenericException - Start date cannot be later in time than end date!

Steps to Reproduce:
Cannot reproduce locally

Expected Results:
No exception

Actual Results:
Exception

Regression:
New to iOS 10 (makes sense since this is in the new NSURLSessionTaskMetrics code

Notes:
Is there potential risk with clock skew?  FWIW: we do all our timing metrics with mach time to avoid potential issues with timezone traversals, regional settings changes or daylight savings time changes.

Comments

I can easily repro this crash on iOS 10.0 and 10.1 by manually adjusting the time in the device (via Settings app) while an NSURLSessionDataTask is in progress. It seems to be already fixed on iOS 10.2 beta 3 though.

I've implemented a quick workaround using swizzling. It's not pretty but it seems to work, i.e. avoids the crash at the expense of not having task metrics available for the task(s) affected by the date/time change.

@interface NSURLSessionTask(YourCategoryName)
@property double startTime;
@end

@interface NSURLSessionTaskMetrics()
- (instancetype)_initWithTask:(NSURLSessionTask *)task;
@end

@interface NSURLSessionTaskMetrics(YourCategoryName)
- (instancetype)xxx_initWithTask:(NSURLSessionTask *)task;
@end

@implementation NSURLSessionTaskMetrics(YourCategoryName)

- (instancetype)xxx_initWithTask:(NSURLSessionTask *)task
{
    if ([NSDate timeIntervalSinceReferenceDate] - task.startTime < 0)
    {
        CFRelease((__bridge CFTypeRef)(self));
        return nil;
    }

    return [self xxx_initWithTask:task];
}

@end

Method originalInit = class_getInstanceMethod([NSURLSessionTaskMetrics class], @selector(_initWithTask:));
Method xxxInit = class_getInstanceMethod([NSURLSessionTaskMetrics class], @selector(xxx_initWithTask:));

if (originalInit && xxxInit)
{
    method_exchangeImplementations(originalInit, xxxInit);
}

Not heavily tested, so use at your own risk.


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!