Skip to content

Fix GCHandle leak in OSX SafeDeleteSslContext#128325

Open
liveans wants to merge 1 commit into
dotnet:mainfrom
liveans:liveans/fix-safedeletesslcontext-gchandle-leak
Open

Fix GCHandle leak in OSX SafeDeleteSslContext#128325
liveans wants to merge 1 commit into
dotnet:mainfrom
liveans:liveans/fix-safedeletesslcontext-gchandle-leak

Conversation

@liveans
Copy link
Copy Markdown
Member

@liveans liveans commented May 18, 2026

Note

This PR was authored with help from GitHub Copilot.

The GCHandle allocated in SafeDeleteSslContext.SslSetConnection was never freed, leaking one GCHandle table slot per SSL context on the legacy macOS SecureTransport path.

Freeing it in SafeDeleteSslContext.Dispose races with in-flight native Read/Write callbacks (reliably reproduced as a Debug.Assert crash in SslStreamSniTest.SslStream_ServerCallbackAndLocalCertificateSelectionSet_Throws). Instead, ownership of the handle is moved into SafeSslHandle and freed from its ReleaseHandle, which only runs after the ref count hits zero and the native SSLContext is released, so no further callbacks can fire.

Tested locally on osx-arm64: System.Net.Security.Unit.Tests (124) and System.Net.Security.Tests functional (4964) all pass.

Fixes #128136

The GCHandle allocated by SslSetConnection so native Read/Write
callbacks can resolve back to the owning SafeDeleteSslContext was
never freed, leaking one GCHandle table slot per SSL context created
on macOS (legacy SecureTransport path).

Move ownership of the GCHandle to SafeSslHandle and free it from its
ReleaseHandle override. ReleaseHandle is only invoked after the
SafeHandle ref count reaches zero - i.e. once all outstanding
P/Invokes (and therefore any in-flight native Read/Write callbacks)
have completed - so this is the earliest point at which it is safe
to drop the backreference. Freeing the handle in
SafeDeleteSslContext.Dispose directly races with callbacks that are
still in flight on another thread.

Fixes dotnet#128136

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 18, 2026 13:12
@liveans liveans requested review from a team and rzikm May 18, 2026 13:13
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the macOS SecureTransport PAL to ensure the GCHandle used as the SSLConnectionRef backreference is freed when the underlying SSLContext is released, rather than leaking for the lifetime of the process.

Changes:

  • Store the GCHandle allocated in SafeDeleteSslContext.SslSetConnection on SafeSslHandle.
  • Free the stored GCHandle from SafeSslHandle.ReleaseHandle() to align its lifetime with the native SSL context.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs Allocates the connection GCHandle and transfers ownership to SafeSslHandle before calling into SslSetConnection.
src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs Extends SafeSslHandle to retain and free the connection GCHandle during ReleaseHandle().

Comment on lines +200 to 202
sslContext.SetConnectionGCHandle(handle);

Interop.AppleCrypto.SslSetConnection(sslContext, GCHandle.ToIntPtr(handle));
// ReleaseHandle override, which only runs after all outstanding
// P/Invokes (and therefore any in-flight Read/Write callbacks)
// have completed. Freeing it here in Dispose would race with
// callbacks that are still in flight on another thread.
GCHandle handle = GCHandle.Alloc(this, GCHandleType.Weak);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this meant to be a weak handle? Target of weak handles can become null, but the Read/Write methods do not expect it.

Copy link
Copy Markdown
Member Author

@liveans liveans May 18, 2026

Choose a reason for hiding this comment

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

Probably not. @rzikm are you aware if there is any historical reason for it to be Weak handle?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

no Idea, the handle was added in #55947, @MaximLipnin do you remember?

I am fine with just changing it into strong GC handle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GCHandle leak in OSX Pal SafeDeleteSslContext

5 participants