diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs index 4d6fb38093c4c2..3ee769a26c4fae 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs @@ -460,6 +460,13 @@ namespace System.Net { internal sealed class SafeSslHandle : SafeHandle { + // Backreference used by AppleCryptoNative_SslSetConnection so native + // Read/Write callbacks can resolve the owning SafeDeleteSslContext. + // Owned here so the lifetime is tied to ReleaseHandle, which only + // runs once all outstanding P/Invokes (and therefore any in-flight + // callbacks) have completed. + private GCHandle _connectionGCHandle; + public SafeSslHandle() : base(IntPtr.Zero, ownsHandle: true) { @@ -470,10 +477,18 @@ internal SafeSslHandle(IntPtr invalidHandleValue, bool ownsHandle) { } + internal void SetConnectionGCHandle(GCHandle handle) + { + Debug.Assert(!_connectionGCHandle.IsAllocated, "Connection GCHandle already set"); + _connectionGCHandle = handle; + } + protected override bool ReleaseHandle() { Interop.CoreFoundation.CFRelease(handle); SetHandle(IntPtr.Zero); + _connectionGCHandle.Dispose(); + return true; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index c94808ea519827..a26029c9f6849f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -191,9 +191,10 @@ private static SafeSslHandle CreateSslContext(SslAuthenticationOptions sslAuthen private void SslSetConnection(SafeSslHandle sslContext) { - GCHandle handle = GCHandle.Alloc(this, GCHandleType.Weak); + var handle = new GCHandle(this); + sslContext.SetConnectionGCHandle(handle); - Interop.AppleCrypto.SslSetConnection(sslContext, GCHandle.ToIntPtr(handle)); + Interop.AppleCrypto.SslSetConnection(sslContext, GCHandle.ToIntPtr(handle)); } public override bool IsInvalid => _sslContext?.IsInvalid ?? true; @@ -220,8 +221,7 @@ protected override void Dispose(bool disposing) [UnmanagedCallersOnly] private static unsafe int WriteToConnection(IntPtr connection, byte* data, void** dataLength) { - SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target; - Debug.Assert(context != null); + SafeDeleteSslContext context = GCHandle.FromIntPtr(connection).Target; // We don't pool these buffers and we can't because there's a race between their us in the native // read/write callbacks and being disposed when the SafeHandle is disposed. This race is benign currently, @@ -255,8 +255,7 @@ private static unsafe int WriteToConnection(IntPtr connection, byte* data, void* [UnmanagedCallersOnly] private static unsafe int ReadFromConnection(IntPtr connection, byte* data, void** dataLength) { - SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target; - Debug.Assert(context != null); + SafeDeleteSslContext context = GCHandle.FromIntPtr(connection).Target; try {