Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ extension URLSession {
fileprivate var timeoutSource: _TimeoutSource? = nil
private var reentrantInUpdateTimeoutTimer = false

// Only use serialization for OpenSSL < 1.1.0 which has race conditions during cleanup
private static let _needsCleanupSerialization: Bool = {
let version = CFURLSessionSSLVersionInfo()
return version.major < 1 || (version.major == 1 && version.minor < 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since curl can be built with TLS libraries other than OpenSSL, and those libraries would return {0, 0, 0} here, we'd be performing cleanup serialization that wouldn't have happened before/might not be needed. Would we want to add some way to check that SSL version parsing did succeed and is in fact OpenSSL before checking the version number?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I missed adding a comment that I was thinking "if the SSL library is not known, then serialise cleanup". But true, it makes sense to not be unnecessarily defensive in this case. So I'll refactor as needed.

}()

// Process-wide cleanup lock
private static let _cleanupLock = NSLock()

init(configuration: URLSession._Configuration, workQueue: DispatchQueue) {
queue = DispatchQueue(label: "MultiHandle.isolation", target: workQueue)
setupCallbacks()
Expand All @@ -58,7 +67,14 @@ extension URLSession {
easyHandles.forEach {
try! CFURLSessionMultiHandleRemoveHandle(rawHandle, $0.rawHandle).asError()
}
try! CFURLSessionMultiHandleDeinit(rawHandle).asError()

if Self._needsCleanupSerialization {
Self._cleanupLock.lock()
try! CFURLSessionMultiHandleDeinit(rawHandle).asError()
Self._cleanupLock.unlock()
} else {
try! CFURLSessionMultiHandleDeinit(rawHandle).asError()
}
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions Sources/_CFURLSessionInterface/CFURLSessionInterface.c
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,22 @@ CFURLSessionCurlVersion CFURLSessionCurlVersionInfo(void) {
return v;
}

CFURLSessionSSLVersion CFURLSessionSSLVersionInfo(void) {
curl_version_info_data *info = curl_version_info(CURLVERSION_NOW);
CFURLSessionSSLVersion version = {0, 0, 0};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to my comment above, maybe we could add a boolean to the struct like isOpenSSL and set that to true if parsing succeeds below? An enum for different TLS libraries could be nice, but I don't think it's really needed since we're just special casing an outdated version of OpenSSL.


if (info && info->ssl_version) {
// Parse OpenSSL version string like "OpenSSL/1.0.2k-fips" or "OpenSSL/1.1.1"
const char *ssl_str = info->ssl_version;
if (strncmp(ssl_str, "OpenSSL/", 8) == 0) {
ssl_str += 8; // Skip "OpenSSL/"
sscanf(ssl_str, "%d.%d.%d", &version.major, &version.minor, &version.patch);
}
}

return version;
}


int const CFURLSessionWriteFuncPause = CURL_WRITEFUNC_PAUSE;
int const CFURLSessionReadFuncPause = CURL_READFUNC_PAUSE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,13 @@ typedef struct CFURLSessionCurlVersion {
} CFURLSessionCurlVersion;
CF_EXPORT CFURLSessionCurlVersion CFURLSessionCurlVersionInfo(void);

typedef struct CFURLSessionSSLVersion {
int major;
int minor;
int patch;
} CFURLSessionSSLVersion;
CF_EXPORT CFURLSessionSSLVersion CFURLSessionSSLVersionInfo(void);


CF_EXPORT int const CFURLSessionWriteFuncPause;
CF_EXPORT int const CFURLSessionReadFuncPause;
Expand Down