Leak when calling CFNetworkExecuteProxyAutoConfigurationURL on Mac

516 views Asked by At

I am using CFNetwork APIs to detect OS proxy settings. My setup is heavily based on this: https://github.com/adobe/chromium/blob/master/net/proxy/proxy_resolver_mac.cc which is pretty much the same as this: https://developer.apple.com/library/archive/samplecode/CFProxySupportTool/Introduction/Intro.html

I use CFNetworkCopyProxiesForURL to get the list of proxies, and for the PAC types, use CFNetworkExecuteProxyAutoConfigurationURL to fetch and execute the PAC script, immediately running the run loop on the current thread, which is already not on the ui thread.

This all works properly, and I have carefully combed through making sure that I am following the create rule to properly release results. However, when using Xcode instruments, I'm seeing that std::shared_ptrs to PAC::PACClient are leaked by _CFNetworkExecuteProxyAutoConfigurationURLDelegated. Since that object is never exposed to me, I'm not sure how I can control its release, but it is leaking. This is only an issue with fetching PAC files, explicit proxies do not leak. I have tried adding redundant CFRelease calls on all the CFDictionaries and such exposed to me to see if something was being over retained, but it didn't make a difference to the PACClient leak.

This is on Mac in a cpp file, not objective C, in a project with ARC on.

Has anyone encountered this leak and know how to prevent it?

Below is the snippet that executes the lookup, which is all the same steps from the above projects.

struct PACRequestInfo {
    CFURLRef url; // Caller gets this from a dictionary, doesn't need release
    CFURLRef scriptURL; // Caller gets this from a dictionary, doesn't need release
    CFMutableArrayRef proxies; // Reference to a dictionary that is released by the caller
};

void resultCallback(void* client, CFArrayRef proxies, CFErrorRef error) {
    // Error handling removed for brevity
    if (CFTypeRef* resultPtr = (CFTypeRef*) client)
        *resultPtr = CFRetain(proxies);

    CFRunLoopStop(CFRunLoopGetCurrent());
}

// Provided PACRequestInfo is created on the stack by the caller
void doPACRequest(const PACRequestInfo& info) {
    CFTypeRef result = nullptr;
    CFStreamClientContext context = { 0, &result, nullptr, nullptr, nullptr };
    // Scoped ptr not shown but just calls CFRelease on destruction
    CFScopedPtr<CFRunLoopSourceRef> runLoopSource(CFNetworkExecuteProxyAutoConfigurationURL(info.scriptURL, info.url, resultCallback, &context));
    if (runLoopSource) {
        const static CFStringRef  kPrivateRunLoopMode = CFSTR("myprivateloop");
        CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kPrivateRunLoopMode);
        CFRunLoopRunInMode(kPrivateRunLoopMode, 1.0e10, false);
        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, kPrivateRunLoopMode);

        if (result && CFGetTypeID(result) == CFArrayGetTypeID()) {
            CFArrayRef resultArray = (CFArrayRef) result;
            CFArrayAppendArray(info.proxies, resultArray, CFRangeMake(0, CFArrayGetCount(resultArray)));
        }
    }

    // Retain was called on this value during ResultCallback.
    if (result) {
        CFRelease(result);
    }
}
0

There are 0 answers