- (NSString*)encodeURL:(NSString *)string
{
NSString *newString = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, CFSTR(":/?#[]#!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
if (newString)
{
return newString; // <-- potential leak here
}
return #"";
}
I'm not familiar with CFTypes (other than knowing what they are). This is code I got from the internet and have had to finagle a bit to get it to work in ARC. I'm getting a potential leak warning and I'm not sure how to fix it. Suggestions?
Yes, this is a memory leak. You meant to use CFBridgingRelease() rather than __bridge.
The object created by CFURLCreateStringByAddingPercentEscapes has an extra retain on it because it includes Create. You need to transfer that object to ARC, letting it know to add an extra release, that's what CFBridgingRelease() does.
NSString *newString =
CFBridgingRelease(
CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(__bridge CFStringRef)string,
NULL,
CFSTR(":/?#[]#!$ &'()*+,;=\"<>%{}|\\^~`"),
kCFStringEncodingUTF8));
You do use __bridge for the passed-in string because you're not transferring it to Core Foundation. You're just asking Core Foundation to use it while ARC continues to own it. When you "transfer" ownership, you generally mean "this object used to be Core Foundation, and now it's ARC" (or vice versa). That's what's happening to newString.
I swapped the long NS-to-CF encoding function with the result just to make it shorter.
return a autoreleased version of the string, CFStringRef can be converted to NSString and vice versa, which also means that you can treat it like a normal NSString and thus autorelease it (if you are interested in this you should read about the toll free bridging between parts of Foundation and Core Foundation)
Another way is using __bridge_transfer instead of __bridge like this:
NSString *newString = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, CFSTR(":/?#[]#!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
See more informations in this question: Correct bridging for ARC?
Related
I am using a technique outlined in the book Hacking and Securing iOS Applications (relevant section here) to wipe the underlying buffer of a NSString as shown below.
NSString *s = [NSString stringWithFormat:#"Hello"];
unsigned char *text = (unsigned char*)CFStringGetCStringPtr((CFStringRef)s, CFStringGetSystemEncoding());
if (text != NULL)
{
memset(text, 0, [s length]);
}
This works, unless the string is a certain value.
// The following crashes with EXC_ACCESS_ERROR on memset
NSString *s = [NSString stringWithFormat:#"No"];
NSString *s = [NSString stringWithFormat:#"Yes"];
// These work fine though
NSString *s = [NSString stringWithFormat:#"Hello"];
NSString *s = [NSString stringWithFormat:#"Do"];
NSString *s = [#"N" stringByAppendingString:#"o"];
It looks like certain strings are not created on the heap but is optimized by making it point to a read-only string table even if the string is created on the heap.
Indeed constant string are not created on the heap and are in read-only memory. This includes a few that look like runtime but are made compile-time constants, your examples are such statements.
With this statement fragment there is no reason not to make it a compile-time constant.
[NSString stringWithFormat:#"No"]
it is equivalent to:
#"No"
Suggestion, file a bug report requesting a secure string class, I have. Several have been filed and I have been told that if there are enough (whatever that amount might be) are files it will implement it.
It is possible to subclass NSString, not easy but you will have compete control of the actual buffer and it should not be subject to possible failure due to Apple changing the implementation detail. I have done that successfully.
NSString *s = [NSString stringWithFormat:#"No"]; will be optimised to NSString *s = #"No"; because there are no substitutions in the format. Assigning a literal will give you a pointer to the readonly text segment of the loaded binary.
NSString *s = [#"N" stringByAppendingString:#"o"]; will create a new string on the heap and return a reference to it. The heap is read-write even though the datatype NSString is readonly.
When you get the CString pointer to the underlying data it's pointing either into read-only data in the first case, or read-write data in the second. The memset will fail on the readonly memory, but succeed on the read-write.
I am trying to convert a CFUUIDRef to a NSString *.
Before, I used the following code, and worked fine.
CFStringRef str = CFUUIDCreateString(NULL, _uuid); # _uuid is of type CFUUIDRef
return (__bridge NSString *) str;
However, after a recent update on Xcode (or other thing that I didn't notice?), the above code gives me the error:
Use of undeclared identifier '__bridge'
So have I did something wrong? How could I solve it?
=== UPDATED ===
The full code:
+ (NSString *)uuidToString:(CFUUIDRef)_uuid {
CFStringRef str = CFUUIDCreateString(NULL, _uuid); # _uuid is of type CFUUIDRef
return (__bridge NSString *) str;
}
The uuid is generated by:
uuid = CFUUIDCreate(NULL);
__bridge is only defined with ARC (Automatic Reference Counting) enabled. It is used to "transfer objects in and out of ARC control". (Source)
To turn on ARC, go to your build settings and set Objective-C Automatic Reference Counting to Yes.
Or, if you do not want to use ARC, simply remove __bridge and it should work fine.
I am trying to convert a CFUUIDRef to a NSString *.
Before, I used the following code, and worked fine.
CFStringRef str = CFUUIDCreateString(NULL, _uuid); # _uuid is of type CFUUIDRef
return (__bridge NSString *) str;
However, after a recent update on Xcode (or other thing that I didn't notice?), the above code gives me the error:
Use of undeclared identifier '__bridge'
So have I did something wrong? How could I solve it?
=== UPDATED ===
The full code:
+ (NSString *)uuidToString:(CFUUIDRef)_uuid {
CFStringRef str = CFUUIDCreateString(NULL, _uuid); # _uuid is of type CFUUIDRef
return (__bridge NSString *) str;
}
The uuid is generated by:
uuid = CFUUIDCreate(NULL);
__bridge is only defined with ARC (Automatic Reference Counting) enabled. It is used to "transfer objects in and out of ARC control". (Source)
To turn on ARC, go to your build settings and set Objective-C Automatic Reference Counting to Yes.
Or, if you do not want to use ARC, simply remove __bridge and it should work fine.
I have a scenario in an iOS application where manipulating a very large NSString instance (an HTTP response, upwards of 11MB) results in multiple large intermediaries being in memory at once, since the SDK methods I am calling return new autoreleased instances. What is the best approach to take here?
For example, assuming that largeString is an autoreleased NSString instance:
NSArray *partsOfLargeString = [largeString componentsSeparatedByString:separator];
for (NSString *part in partsOfLargeString) {
NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
}
It would be great if there were non-autoreleased equivalents to componentsSeparatedByString or stringByTrimmingCharactersInSet, but I'm not looking to implement these myself.
To my knowledge, there isn't a way to "force" release an object that has already been added to an autorelease pool. I know that I can create and use my own autorelease pool here, but I'd like to be extremely granular and having autorelease pools around individual statements definitely isn't a very scalable approach.
Any suggestions are much appreciated.
As Bill said, I’d first try to have an autorelease pool for each loop iteration, e.g.:
for (NSString *part in partsOfLargeString) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
…
[pool drain];
}
or, if you’re using a recent enough compiler:
for (NSString *part in partsOfLargeString) {
#autoreleasepool {
NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
…
}
}
If that’s still not acceptable and you do need to release objects in a more granular fashion, you could use something like:
static inline __attribute__((ns_returns_retained))
id BICreateDrainedPoolObject(id (^expression)(void)) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
id object = expression();
[object retain];
[pool drain];
return object;
}
#define BIOBJ(expression) BICreateDrainedPoolObject(^{return (expression);})
which evaluates the expression, retains its result, releases any ancillary autoreleased objects and returns the result; and then:
for (NSString *part in partsOfLargeString) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *trimmedPart = BIOBJ([part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]);
NSData *data = BIOBJ([trimmedPart dataUsingEncoding:NSUTF8StringEncoding]);
[trimmedPart release];
// do something with data
[data release];
…
[pool drain];
}
Note that, since the function returns a retained object, you’re responsible for releasing it. You’ll have control over when to do that.
Feel free to choose better names for the function and macro. There might be some corner cases that should be handled but it should work for your particular example. Suggestions are welcome!
First, you shouldn't need to parse responses from an HTTP server in this fashion. Parsing HTTP responses (including parsing HTML) is a solved problem and attempting to parse it using raw string manipulation will lead to fragile code that can easily be crashed with seemingly innocuous server side changes.
Autorelease pools are pretty cheap. You could surround the body [inside] of the for with #autoreleasepool {... that code ...} and it'll probably both fix your high-water issue and have negligible performance impact [compared to raw string manipulation].
Beyond that, your summary is correct -- if there isn't a non-autoreleasing variant in the 'kit, then you'd have to re-invent the wheel. With that said, it is fairly typical that the lack of a non-autoreleasing variant is not an oversight on the part of the designer. Instead, it is likely because there are better tools available for achieving the sort of high-volume solution that would also require finer grained memory management.
I have some sensitive data I want to clear directly after use. Currently, the sensitive data is in the form of NSString. NSString is in my understanding immutable, meaning that I can't really clear the data. NSMutableString seems more appropriate, though, as it is mutable and has methods like replaceCharactersInRange and deleteCharactersInRange. I have no knowledge of the implementation details so I wonder if NSMutableString would serve my purpose?
I would be afraid NSMutableString would try to optimize and leave the string in memory. If you want more control try allocating your own memory then create an NSString with it. If you do that you can overwrite the memory before you release it.
char* block = malloc(200);
NSString* string = [[NSString alloc] initWithBytesNoCopy:length:encoding:freeWhenDone];
//use string
memset(block, 0, 200);// overwrite block with 0
[string release];
free(block);
You need to wipe the c pointer with zeros with a memset function however a memset call can be optimized out by the compiler, see What is the correct way to clear sensitive data from memory in iOS?
So the code could be something like this:
NSString *string = #"hi";
unsigned char *stringChars = (unsigned char *)CFStringGetCStringPtr((CFStringRef)string, CFStringGetSystemEncoding());
safeMemset(stringChars, 0, [string length]);
But be careful clearing the underlying c pointer of an NSString. On a device for example, if the string contains the word "password", the underlying c pointer just reuses or points to the same address as used by the system and you will crash by trying to wipe this area of memory.
To be safe you may want to use a char array, not the char pointer, to store your sensitive strings and wipe them after without ever putting it into an NSString object.
If an attacker can read the contents of memory, you are beyond hosed.
-release the string and be done with it. There's no way to know if you've deleted any possible copies of the string in various caches (such as if you draw it to screen, etc).
You probably have much more significant security issues to worry about.
As of iOS9, inner pointer of NSString obtained from the snippet below has become read-only and generates bad access when trying to set the bytes.
unsigned char *stringChars = (unsigned char *)CFStringGetCStringPtr((CFStringRef)string, CFStringGetSystemEncoding());
It is possible with NSMutableString but then if you have another NSString source, say from a textfield, that source will still be in memory and you're still out of luck.
If you are creating a new NSString, The best way is implement your own String class with underlying byte array. Provide a method to create NSString copies using the underlying byte array as the inner pointer.:
-(NSString *)string
{
return [[NSString alloc] initWithBytesNoCopy:_buff length:_length encoding:NSUTF8StringEncoding freeWhenDone:NO];
}
// Will prematurely wipe data and all its copies when called
- (void)clear
{
// Volatile keyword disables compiler's optimization
volatile unsigned char *t = (unsigned char *)_buff;
int len = _length;
while (len--) {
*t++ = 0;
}
}
// In case you forget to clear, it will cleared on dealloc
- (void)dealloc
{
[self clear];
free(_buff);
}