Calling CFRelease for a CFHostRef will sometimes crash

490 views Asked by At

This is a secondary question that arose out of a post I made earlier today. I have the method below, which works fine for what I need, but sometimes crashes when I call CFRelease on the hostRef variable. I think it may have to do with the resource being used elsewhere when I'm trying to release it, but as far as I can tell, I'm synchronously resolving the host and I'm not accessing it from another thread.

I tried calling CFHostCancelInfoResolution before CFRelease, but that didn't change the frequency of the crashes. I thought I would post this here to see if there are some assumptions or misconceptions I have that aren't true.

+ (NSArray *) addressesForHostname: (NSString *)hostname {

    CFMutableArrayRef ipAddresses = nil;

    DLog(@"Getting addresses for host name %@", hostname);

    CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)(hostname));
    CFStreamError error;

    BOOL didResolve = CFHostStartInfoResolution(hostRef, kCFHostAddresses, &error); // synchronously get the host.

    if (didResolve) {

        CFArrayRef responseObjects = CFHostGetAddressing(hostRef, NULL);
        long numberOfResponses = CFArrayGetCount(responseObjects);
        ipAddresses = CFArrayCreateMutable(kCFAllocatorDefault, numberOfResponses, &kCFTypeArrayCallBacks);

        for ( int i = 0 ; i < numberOfResponses; ++i ) {

            char * ipAddress = NULL;
            CFDataRef responseObject = CFArrayGetValueAtIndex(responseObjects, i);
            struct sockaddr * currentAddress = (struct sockaddr *) CFDataGetBytePtr(responseObject); // Unwrap the CFData wrapper aound the sockaddr struct

            switch (currentAddress->sa_family) {

                case AF_INET: { // Internetworking AKA IPV4

                    DLog(@"Extracting IPV4 address");
                    struct sockaddr_in * socketAddress = (struct sockaddr_in *) currentAddress;

                    ipAddress = malloc(sizeof(INET_ADDRSTRLEN));
                    inet_ntop(AF_INET,
                              &(socketAddress->sin_addr),
                              ipAddress,
                              INET_ADDRSTRLEN);

                    CFStringRef ipAddressString = CFStringCreateWithCString(kCFAllocatorDefault, ipAddress, kCFStringEncodingASCII);
                    CFArrayInsertValueAtIndex(ipAddresses, i, ipAddressString);

                    break;

                }

                case AF_INET6: { // IPV6

                    DLog(@"Extracting IPV6 address");
                    struct sockaddr_in6 * socketAddress = (struct sockaddr_in6 *) currentAddress;

                    ipAddress = malloc(sizeof(INET6_ADDRSTRLEN));
                    inet_ntop(AF_INET6,
                              &(socketAddress->sin6_addr),
                              ipAddress,
                              INET6_ADDRSTRLEN);

                    CFStringRef ipAddressString = CFStringCreateWithCString(kCFAllocatorDefault, ipAddress, kCFStringEncodingASCII);
                    CFArrayInsertValueAtIndex(ipAddresses, i, ipAddressString);

                    break;
                }

                default:
                    DLog(@"Unsupported addressing protocol encountered. Gracefully ignoring and continuing.");
                    break;
            }

            if(ipAddress != NULL) {
                free(ipAddress);
            }
        }

        CFRelease(responseObjects);

    }

    CFRelease(hostRef);

    return (__bridge_transfer NSArray *) ipAddresses;
}
3

There are 3 answers

1
Ping Xia On

removing CFRelease(responseObjects); shall fix it

0
Rob Napier On

You must follow the Create Rule for Core Foundation objects. If you received the object by calling a function with the words Create or Copy in their names (or if you call CFRetain explicitly), then you must release (CFRelease) the object when you're done with it. If you did not receive the object this way, then you must not release the object.

There are several mistakes in your code. First, the one you're finding, which is around responseObjects. You fetch this object using:

CFDataRef responseObject = CFArrayGetValueAtIndex(responseObjects, i);

The function does not have Create or Copy in its name. You must not call CFRelease on it.

However, you also call this:

ipAddresses = CFArrayCreateMutable(kCFAllocatorDefault, numberOfResponses, &kCFTypeArrayCallBacks);

and in a couple of places:

CFStringRef ipAddressString = CFStringCreateWithCString(kCFAllocatorDefault, ipAddress, kCFStringEncodingASCII);

You must call CFRelease on these objects before they go out of scope or you will leak them.

0
loukerx On

I think CFStreamError error; may cause this issue.

Try to declare error to null?

// synchronously get the host.
BOOL didResolve = CFHostStartInfoResolution(hostRef, kCFHostAddresses, NULL);