I have an app for Mac and iOS that uses .plist files to sync data between devices using iCloud. Every once in a while I get an email from someone that's getting repeated crashes on a specific device. (Other devices are fine.) In every case the app crashed while trying to read in a file from iCloud, using NSPropertyListSerialization to convert the data to a dictionary. In every case the problem is solved by having the user delete the iCloud data for the app. The same files get synced back in, and everything works fine again.
The specific example I'll use here is from the Mac version, but I've had almost identical crashes on iOS. From the crash report:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: 0x000000000000000a, 0x0000000101ee2000
VM Regions Near 0x101ee2000:
VM_ALLOCATE 0000000101ee1000-0000000101ee2000 [ 4K] rw-/rwx SM=PRV
--> mapped file 0000000101ee2000-0000000101ee3000 [ 4K] r--/rwx SM=COW /Users/USER/Library/Mobile Documents/PB4R74AA4J~com~junecloud~Notefile/*/*.notefile
shared memory 0000000101ee3000-0000000101ee4000 [ 4K] rw-/rw- SM=SHM
And then:
Thread 7 Crashed:
0 libsystem_c.dylib 0x0000000105157013 bcmp + 19
1 com.apple.CoreFoundation 0x0000000101bbf4b0 __CFBinaryPlistGetTopLevelInfo + 80
2 com.apple.CoreFoundation 0x0000000101bbf36d __CFTryParseBinaryPlist + 93
3 com.apple.CoreFoundation 0x0000000101bbede2 _CFPropertyListCreateWithData + 146
4 com.apple.CoreFoundation 0x0000000101bcbdb0 CFPropertyListCreateWithData + 112
5 com.apple.Foundation 0x0000000101568b89 +[NSPropertyListSerialization propertyListWithData:options:format:error:] + 94
6 com.junecloud.Notefile-Helper 0x000000010107ad06 -[JUNNoteDocument readFromData:ofType:error:] + 64
7 com.apple.AppKit 0x000000010268d507 -[NSDocument readFromURL:ofType:error:] + 546
8 com.apple.AppKit 0x000000010228e3c8 -[NSDocument _initWithContentsOfURL:ofType:error:] + 135
9 com.apple.AppKit 0x000000010228e074 -[NSDocument initWithContentsOfURL:ofType:error:] + 262
10 com.junecloud.Notefile-Helper 0x000000010107cad8 -[JUNSyncDocument initWithFileURL:] + 213
11 com.junecloud.Notefile-Helper 0x0000000101079ec7 -[NotefileAppDelegate documentForURL:] + 68
12 com.junecloud.Notefile-Helper 0x00000001010825cb -[JUNSyncManager documentForURL:] + 76
13 com.junecloud.Notefile-Helper 0x000000010107d43c -[JUNSyncInOperation main] + 1340
14 com.apple.Foundation 0x0000000101563cd2 __NSThread__main__ + 1345
15 libsystem_c.dylib 0x00000001051697a2 _pthread_start + 327
16 libsystem_c.dylib 0x00000001051561e1 thread_start + 13
Thread 7 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000062 rbx: 0x000000000000007b rcx: 0x000000010c2be868 rdx: 0x0000000000000007
rdi: 0x0000000101d1e151 rsi: 0x0000000101ee2000 rbp: 0x000000010c2be810 rsp: 0x000000010c2be7b8
r8: 0x000000010c2be870 r9: 0x0000000000000000 r10: 0x00007ff841c28900 r11: 0x00007ff841c186d8
r12: 0x000000010c2be870 r13: 0x0000000101ee2000 r14: 0x000000010c2beb00 r15: 0x000000010c2be980
rip: 0x0000000105157013 rfl: 0x0000000000010202 cr2: 0x0000000101ee2000
Logical CPU: 2
Here's the relevant bit of code where it's crashing:
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
BOOL success = NO;
NSDictionary *dictionary = nil;
NSError *error = nil;
id plist = [NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable format:NULL error:&error];
if (plist && [plist isKindOfClass:[NSDictionary class]]) {
dictionary = plist;
} else {
NSLog(@"Error opening document: %@ %@",error,[error userInfo]);
if (outError != NULL) *outError = error;
}
// Then it does some things with the dictionary if it's not nil
return success;
}
Am I correct in thinking that NSPropertyListSerialization is just choking on some corrupt data, or is the problem more likely in my own code? If the problem is with NSPropertyListSerialization, is there anything I can do to prevent the app from crashing, and deal with the problem more appropriately?
If the problem is likely to be in my own code, what could I do to track down the cause? This would be much easier if I could duplicate the problem myself, but I've never seen this crash on my own devices, and obviously I can't expect a user to give me access to their iCloud account.
Update As requested, here's a bit of JUNSyncDocument. On iOS this is a UIDocument subclass, on Mac it's an NSDocument subclass.
- (id)initWithFileURL:(NSURL *)url {
#if TARGET_OS_IPHONE
self = [super initWithFileURL:url];
return self;
#else
NSError *error = nil;
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
if ((self = [super initWithContentsOfURL:url ofType:[self documentType] error:&error])) {
self.fileURL = url;
self.hasUndoManager = NO;
} else NSLog(@"Error initializing existing document: %@ %@",url,error);
} else {
if ((self = [super initWithType:[self documentType] error:&error])) {
self.fileURL = url;
self.hasUndoManager = NO;
} else NSLog(@"Error initializing new document: %@",error);
}
return self;
#endif
}
This seems messy, and I wouldn't be surprised if I'm doing something stupid here. It works fine in most cases though. That gets called from NotefileAppDelegate:
- (JUNSyncDocument *)documentForURL:(NSURL *)url {
JUNNoteDocument *document = [[JUNNoteDocument alloc] initWithFileURL:url];
return document;
}
Which in turn is called by JUNSyncManager:
- (JUNSyncDocument *)documentForURL:(NSURL *)url {
return [self.delegate documentForURL:url];
}
Which is called by JUNSyncInOperation:
JUNSyncDocument *document = [self.delegate documentForURL:self.url];
I just realized I can get rid of that ridiculous delegate chain by using NSClassFromString, but I don't know if that will affect the problem or not.
Looking at the source of __CFTryParseBinaryPlist and __CFBinaryPlistGetTopLevelInfo, which are open source.
It looks like the memcmp (bcmp) that is crashing is at the very beginning where it checks first few bytes of the data for the binary plist header. It wouldn't get that far if CFDataGetLength was <= 8 bytes, so it's not a buffer underflow. It's possible CFDataGetBytePtr returned nil, but I don't see how that could happen if the length was > 8. Most likely, the data pointer has gone invalid.
I could tell more you posted the register contents from the crash report. Also, post the code of how it's creating the data (-[JUNSyncDocument initWithFileURL:] and -[NotefileAppDelegate documentForURL:].)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With