Object released with CFRelease causes obvious crash, but only rarely - objective-c

I have the following method:
+ (NSString*) getMD5HashFromFile:(NSString*)filePath {
CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
NSString *hashStr = (NSString*)md5hash;
CFRelease(md5hash);
return hashStr;
}
I was getting random crashes on the Simulator, about 1 in 20-30 executions. The fact that this wasn't consistent didn't help me dig deeper before.
Now that I see the code again, it seems obvious that md5hash gets released before being returned, which means the returned object is invalidated. The returned value is used in another method in a consistent way that crashes sometimes, but not always. My question is why this only happens rarely and not always.
Does it have something to do with the mix of Obj-C and C code and the way autorelease pools work?
Note: The bug seems to be fixed by using NSString *hashStr = [NSString stringWithString:(NSString*)md5hash], which makes total sense to me.

Just because a piece of memory is released and deallocated doesn't mean that it's immediately returned to the OS. Your application can hold onto it for an arbitrary period of time based on numerous factors and at several layers. The OS has more important things to do sometimes than reclaim every piece of memory you let go of and might ask for again in half a second. Accessing memory that you've called free() on, but technically own, does not generate a signal. This is why MallocScribble exists. It overwrites memory that you free with trash (0x55) so that it's more obvious when you use freed memory.
Try the following:
char *foo = malloc(100);
strcpy(foo, "stuff");
free(foo);
printf("%s", foo);
Most of the time that'll work fine, despite being completely wrong. Now, edit your Scheme>Diagnostics and Enable Scribble. Re-run and you'll see a bunch of "U" (0x55) indicating that you're reading nonsense. But it still won't crash.
You may be interested in Matt Gallagher's A look at how malloc works on the Mac for a bit more on the topic.

CFRelease argument must not be NULL.
If CFRelease argument is NULL, this will cause a runtime error and
your application will crash
if(md5hash)
CFRelease(md5hash);

+(NSString*) getMD5HashFromFile:(NSString*)filePath {
CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
NSString *hashStr = [(NSString*)md5hash copy];
CFRelease(md5hash);
return [hashStr autorelease];
}
make sure to retain the returned value in the caller if you need to hang on to it for any length of time.

Related

Non-deterministic crash in BlocksKit bk_apply block

I have a function that constructs an NSMutableDictionary using bk_apply, a method provided by the third-party block utility library BlocksKit. The function's test suite usually passes just fine, but once every couple runs it crashes.
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
[inputSet bk_apply:^(NSString *property) {
NSString *localValueName = propertyToLocalName[property];
NSObject *localValue = [self valueForKey:localValueName];
result[property] = localValue ?: defaults[property]; // Crash
// Convert all dates in result to ISO 8601 strings
if ([result[property] isKindOfClass:[NSDate class]]) { // Crash
result[property] = ((NSDate *)result[property]).ISODateString; // Crash
}
}];
The crash always happens on a line where result is referenced, but it's not the same line every time.
Examining the contents of result in the debugger, I've seen very strange values like
po result
{
val1 = "Some reasonable value";
val2 = "Also reasonable value";
(null) = (null);
}
It's impossible for an NSDictionary to have null keys or values, so clearly some invariant is being violated.
What is causing this crash and how do I fix it?
From the BlocksKit documentation for bk_apply:
Enumeration will occur on appropriate background queues. This will
have a noticeable speed increase, especially on dual-core devices, but
you must be aware of the thread safety of the objects you message
from within the block.
The code above is highly unsafe with respect to threading, because it reads from and writes to a mutable variable on multiple threads.
The intermittent nature of the crash comes from the fact that the thread scheduler is non-deterministic. The crash won't happen when several threads accessing shared memory happen to have their execution scheduled in sequence rather than in parallel. It is therefore possible to "get lucky" some or even most of the time, but the code is still wrong.
The debugger printout is a good example of the danger. The thread that's paused is most likely reading from result while another thread performs an insertion.
NSMutableDictionary insertions are likely not atomic; example steps might be,
allocate memory for the new entry
copy the entry's key into the memory
copy the entry's value into the memory
If you read the dictionary from another thread between steps 1 and 2, you will see an entry for which memory has been allocated, but the memory contains no values.
The simplest fix is to switch to bk_each. bk_each does the same thing as bk_apply but it's implemented in a way that guarantees sequential execution.

Why does this ternary operation causes memory growth

The following line causes memory growth (no releases, only one malloc line in instruments) when testing with mark generation feature of allocations instrument
- (instancetype)initWithTitle:(NSString *)title andImageNamed:(NSString *)imageName andButtonProperties:(NOZSKButtonNodeProperties *)buttonProperties
{
...
textNode.text = buttonProperties.buttonTitleIsUppercase ?
[title uppercaseStringWithLocale:[NSLocale currentLocale] ] : title;
...
}
Here is the code that calls it
NOZSKButtonNodeProperties *props = [self getThreeButtonProps];
...
props.buttonTitleIsUppercase = YES;
...
// this initializer is calling the above initializer by passing nil for imageName arg
NOZSKButtonNode *btn = [[NOZSKButtonNode alloc] initWithTitle:#"Play Again" andButtonProperties:props];
-uppercaseStringWithLocale: makes an upper-case copy of the title string. This requires allocation. And although you didn't show this, I assume textNode both retains its text property and has a wider scope than the -initWithTitle:andImageNamed:andButtonProperties: method and therefore continues to live afterwards.
Without more complete code it is not possible to be sure, but here is a guess in case it helps.
It sounds like you might be chasing a ghost. Are you seeing increasing memory due to this code across different iterations of your run loop?
Explanation: Even with ARC some memory is placed in the "autorelease pool" rather than being immediately deallocated when no longer required. This is an unfortunate legacy of MRC. ARC is able to avoid some uses of the autorelease pool and deallocate memory quicker, but not all uses.
The autorelease pool is typically emptied once per iteration of your main run loop. If you allocate and release a lot of memory in response to a single event, say in a long loop, it can be worth while wrapping the offending code in an #autorelease { ... } block which creates, and empties on exit, a local autorelease pool. This helps keep the peak memory usage down - it doesn't deallocate any more memory overall, it just does it cleanup sooner.
When you altered your code and saw an apparent improvement you may just have reorganised your code in a way more amenable to ARC's optimisations which reduce use of the autorelease pool, and so memory is deallocated sooner.
You only need to be concerned if (a) memory is increasing across multiple events or (b) you are hitting a too high peak memory use. Under ARC (a) should be rare, while (b) requires locating the source and wrapping it in an #autorelease { ... } block.
HTH
textNode.text = buttonProperties.buttonTitleIsUppercase ?
[title uppercaseStringWithLocale:[NSLocale currentLocale] ] : title;
When you write [title uppercaseStringWithLocale:], this method has to create a new instance in order to change the given string to upper case. This is going to increase the memory usage of your program because this new string has to be allocated.
Also, it would have helped to know whether or not you are using ARC, because I don't think this should be an issue with ARC.

How to zeroize an objective-c object under ARC [duplicate]

On iOS, I was wondering, say if I read user provided password value as such:
NSString* strPwd = UITextField.text;
//Check 'strPwd'
...
//How to clear out 'strPwd' from RAM?
I just don't like leaving sensitive data "dangling" in the RAM. Any idea how to zero it out?
Basically you really can't. There are bugs filed with Apple over this exact issue. Additionally there are problems with UITextField and NSString at a minimum.
To reiterate the comment in a now deleted answer by #Leo Natan:
Releasing the enclosing NSString object does not guarantee the string
bytes are zeroes in memory. Also, if a device is jailbroken, all the
sandboxing Apple promises will be of no use. However, in this case,
there is little one can do, as it is possible to swap the entire
runtime in the middle of the application running, this getting the
password from the memory.
Please file another bug with apple requesting this, the more the better.
Apple Bug Reporter
While NSString doesn't have this capability (for reasons of encapsulation mentioned elsewhere), it shouldn't be too hard to have your app use regular old C-strings, which are just pointers to memory. Once you have that pointer, it's fairly easy to clear things out when you're done.
This won't help with user-entered text-fields (which use NSString-s and we can't change them), but you can certainly keep all of your app's sensitive data in pointer-based memory.
I haven't experimented with it (I don't have a current jailbroken device), but it also might be interesting to experiment with NSMutableString -- something like:
// Code typed in browser; may need adjusting
// keep "password" in an NSMutableString
NSInteger passLength = password.length;
NSString *dummy = #"-";
while (dummy.length < passLength)
{
dummy = [dummy stringByAppendingString: #"-"];
}
NSRange fullPass = NSMakeRange(0, passLength);
[password replaceOccurancesOfString: password
withString: dummy
options: 0
range: fullPass];
NOTE: I have no idea if this does what you want; it's just something I thought of while typing my earlier answer. Even if it does work now, I guess it depends on the implementation, which is fragile (meaning: subject to breaking in the future), so shouldn't be used.
Still, might be an interesting exercise! :)

How to cast from id __autoreleasing * to void ** in ARC?

I'm trying to cast from an id __autoreleasing * to a CFTypeRef * (void **).
I've tried:
id __autoreleasing *arg = [OCMArg setTo:mockData];
CFTypeRef expectedResult = (__bridge CFTypeRef) *arg;
[[[self.mockSecItemService expect] andReturnValue:OCMOCK_VALUE(mockCopyStatus)] copyItemMatching:queryCheck
result:&expectedResult];
But the code crashes when the autorelease pool is drained.
How does one convert to void** in an ARC environment?
I do not know the APIs you are using, so I'm not 100% sure of what's going on. I googled and they seem to be part of OCMock. I downloaded it and (without installing it as I'm not interested) I rapidly browsed the source.
I see something very fishy in that code. Here's how they implement the first method you call:
#implementation OCMArg
....
+ (id *)setTo:(id)value
{
return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease];
}
So they are returning an id* which is really just an id.
To me that's either a nonsense/error or an attempt to manipulate ObjC internals (even if undocumented, the first thing an ObjC object stores is in fact a pointer to the object class and is therefore of type Class which is compatible with id, therefore it somehow is valid to cast a pointer to an object or an id that refers to an object, to Class* or id*). I have no time or interest in going and studying the whole API to figure out why they do that. They may actually have a good reason (for example if you only pass that result to another API that knows what it's supposed to be, but you are doing more than that here). Instead of studying OCMock I'll try to explain you what is happening as far as I can say (ObjC and ARC).
id __autoreleasing *arg = [OCMArg setTo:mockData];
ARC will do absolutely nothing in this line of code.
What that method does you can see above. Class OCMPassByRefSetter is a simple class that just stores the argument after retaining it, so mockData is retained. The OCMPassByRefSetter is autoreleased and will disappear at the next drain (releasing the mockData and making *arg reference to released memory).
Note that arg in fact points to the isa of the OCMPassByRefSetter (the isa is the "first" ivar of any object, it's of type Class and points to the class of the object. But this is undocumented and may change at any time).
CFTypeRef expectedResult = (__bridge CFTypeRef) *arg;
*arg is of type id which is compatible with CFTypeRef, so the cast is valid. You use __bridge so ARC does absolutely nothing.
If arg pointed to a "toll free bridged" CF/Cocoa class this would be perfectly valid code but you'd have to be careful that expectedResult would become invalid at the next drain (it's not retained, but it's live as an autoreleased instance).
[[[self.mockSecItemService expect] andReturnValue:OCMOCK_VALUE(mockCopyStatus)] copyItemMatching:queryCheck
result:&expectedResult];
No idea what this line does. Given the prototype you posted in the comment above, ARC does nothing on the part result:&expectedResult.
You say it's a wrapper around SecItemCopyMatching, but as I understand it it's more than that. If it was just immediately calling SecItemCopyMatching passing it the result: argument, you'd likely be messing things up. But the name expectedResult and the fact that this is OCMock makes me think this is a little more complex than that.
You'll have to investigate it yourself a bit. But remember:
as soon as the current function exits, the argument you passed (&expectedResult) will become invalid as it's a local variable.
as soon as there is a drain, the value of expectedResult will become invalid, as that address points to memory that is going to be deallocated by the drain.
doing anything with the value of expectedResult is likely do be going very wrong as I do not think that a Class qualifies as "toll free bridged".
I suspect, but I may be very wrong, that you are not using the OCMock apis the way they are intended to be used. But on this front I cannot help you, and maybe you are actually doing it right.
Rather than try and figure out how to cast the variable into the correct format (OCMock is doing some complex things internally), I added another method, to handle the conversion.
- (OSStatus)findItemMatching:(NSDictionary *)query result:(id __autoreleasing *)outResult {
NSAssert(outResult, #"outResult is required");
CFTypeRef result = nil;
OSStatus status = [self copyItemMatching:query result:&result];
if (result) {
*outResult = CFBridgingRelease(result);
}
return status;
}

Why I'm getting memory leaks with xmlTextReaderConstValue?

I'm writing my own wrapper class for parsing XML data. Usually I use the Leak Performance Tool to detect suspicios behaviour through forgetting to release allocated memory.
At this time I figured out that the following code (the first line becomes marked by the tool) brings me an enormous memory leak (leaks more the bigger the XML data file becomes).
the following part is used to receive the text inside a Node.
NSString *currentTagValue = [NSString stringWithCString:(char *)xmlTextReaderConstValue(XMLReader) encoding:NSUTF8StringEncoding];
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", [currentTag capitalizedString]]);
[currentItem performSelector:selector withObject:currentTagValue];
If I add
[currentTagValue release]
the memory leaks are gone.
This seems strange to me, because I don't allocate memory for the NSString manually. That's why I thought it would be autoreleased.
The whole situation becomes stranger if I compare the upper code example with the part that is responsible for obtaining the node name.
NSString *currentTagName = [NSString stringWithCString:(char *)xmlTextReaderConstName(XMLReader) encoding:NSUTF8StringEncoding];
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", [currentTagName capitalizedString]]);
Here I dont't have to add a manual release, everything works fine and I'm getting no memory leak.
I'm not sure if my described problem is a side-effect of the xml...ConstValue function (the working part uses xml...ConstName) or if the reason is the performed selector afterwards.
Thanks for reading, I hope anyone can explain it to me.
Are you using libxml2? I haven't used libxml2 yet, but I googled quickly and found this:
http://xmlsoft.org/html/libxml-xmlreader.html
Function: xmlTextReaderConstValue
Returns: the string or NULL if not
available. The result will be
deallocated on the next Read()
operation.
Compare that with xmlTextReaderConstName
Function: xmlTextReaderConstName
Returns: the local name or NULL if not
available, the string is deallocated
with the reader.
It may be a leak in the lib, or a false alarm as the result seems to be on a delayed release (or something entirely different as I have no firsthand experience to say otherwise). Is the program crashing because of the leak or not? If it is not, maybe it's just a false alarm.
Hope it helps.