Foundation.framework: NSData does not perform copy when needed
| Originator: | nikolai.ruhe | ||
| Number: | rdar://14670403 | Date Originated: | 07-Aug-2013 11:30 AM |
| Status: | Closed | Resolved: | 23-Oct-2013 |
| Product: | OS X SDK | Product Version: | OS X 10.8.4 |
| Classification: | Serious bug | Reproducible: | Always |
Summary: When creating an NSData object using `initWithBytesNoCopy:length:freeWhenDone:` the object does not behave safely when creating a copy: -[NSData copy] just returns the same instance. While this behavior is perfectly understandable for standard NSData objects (where the underlying data is immutable and owned by the NSData object) this is not correct in this special case. As the underlying data is not owned by the no-copy-NSData (and could be modified or released at any time) the NSData object is only valid as long as the data is. If some code want's to keep the NSData around it calls `copy` and expects to receive a private, decoupled version of the same data, that does not go away or is mutated from outside. Steps to Reproduce: Here's a code snippet that shows the bug: // ------------------------------------------------ char bytes[4]; strcpy(bytes, "foo"); // wrap the data in an NSData that points to the original data NSData *data = [[NSData alloc] initWithBytesNoCopy:bytes length:strlen(bytes) + 1 freeWhenDone:NO]; NSLog(@"1. data: '%s' (%p)", [data bytes], data); // make an immutable and a mutable copy of the NSData NSData *copyOfData = [data copy]; NSData *mutableCopyOfData = [data mutableCopy]; // overwrite the on stack data strcpy(bytes, "bar"); NSLog(@"2. copyOfData: '%s' (%p)", [copyOfData bytes], copyOfData); NSLog(@"3. mutableCopyOfData: '%s' (%p)", [mutableCopyOfData bytes], mutableCopyOfData); // ------------------------------------------------ Expected Results: copyOfData must refer to a different object than data Actual Results: copyOfData refers to the same object as data (pointers are identical) Notes: While the example above might seem contrived, I experienced this bug in real world. I fed data from an sqlite database into a CGImageSource to create an CGImage out of it. I assume the CGImageSource copies the data to do the decompression of the image lazily. Obviously problems occurred when the image was accessed after the sqlite data was released. The ImageIO framework expected to get its private copy by calling CFDataCreateCopy or similar. To fix the problem I had to create a `mutableCopy` from the no-copy-data.
Comments
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!