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 06ecc8d9ed1d72..6b917e154a8fc3 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 @@ -7,6 +7,7 @@ using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; +using System.Threading; using Microsoft.Win32.SafeHandles; namespace System.Net @@ -19,6 +20,13 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext private Interop.AppleCrypto.SSLWriteFunc _writeCallback; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); + // state values for the _pendingXXX varaibles bellow + private const int NoIOPending = 0; + private const int NativeIOPending =1; + private const int ManagedIOPending = 2; + private const int Disposed = 3; + private int _pendingInput; + private int _pendingOutput; public SafeSslHandle SslContext => _sslContext; @@ -144,8 +152,16 @@ protected override void Dispose(bool disposing) SafeSslHandle sslContext = _sslContext; if (null != sslContext) { - _inputBuffer.Dispose(); - _outputBuffer.Dispose(); + if (Interlocked.Exchange(ref _pendingInput, Disposed) == NoIOPending) + { + _inputBuffer.Dispose(); + } + + if (Interlocked.Exchange(ref _pendingOutput, Disposed) == NoIOPending) + { + _outputBuffer.Dispose(); + } + sslContext.Dispose(); } } @@ -158,6 +174,12 @@ private unsafe int WriteToConnection(void* connection, byte* data, void** dataLe ulong length = (ulong)*dataLength; Debug.Assert(length <= int.MaxValue); + if (Interlocked.Exchange(ref _pendingOutput, NativeIOPending) == Disposed) + { + const int writErr = -20; + return writErr; + } + int toWrite = (int)length; var inputBuffer = new ReadOnlySpan(data, toWrite); @@ -165,6 +187,11 @@ private unsafe int WriteToConnection(void* connection, byte* data, void** dataLe inputBuffer.CopyTo(_outputBuffer.AvailableSpan); _outputBuffer.Commit(toWrite); + if (Interlocked.CompareExchange(ref _pendingOutput, NoIOPending, NativeIOPending) == Disposed) + { + _outputBuffer.Dispose(); + } + // Since we can enqueue everything, no need to re-assign *dataLength. const int noErr = 0; return noErr; @@ -181,10 +208,21 @@ private unsafe int ReadFromConnection(void* connection, byte* data, void** dataL return noErr; } + if (Interlocked.Exchange(ref _pendingInput, NativeIOPending) == Disposed) + { + const int readErr = -19; + return readErr; + } + uint transferred = 0; if (_inputBuffer.ActiveLength == 0) { + if (Interlocked.Exchange(ref _pendingInput, NoIOPending) == Disposed) + { + _inputBuffer.Dispose(); + } + *dataLength = (void*)0; return errSSLWouldBlock; } @@ -195,28 +233,52 @@ private unsafe int ReadFromConnection(void* connection, byte* data, void** dataL _inputBuffer.Discard(limit); transferred = (uint)limit; + if (Interlocked.Exchange(ref _pendingInput, NoIOPending) == Disposed) + { + _inputBuffer.Dispose(); + } + *dataLength = (void*)transferred; return noErr; } internal void Write(ReadOnlySpan buf) { + if (Interlocked.Exchange(ref _pendingInput, ManagedIOPending) == Disposed) + { + throw new ObjectDisposedException(nameof(SafeDeleteSslContext)); + } + _inputBuffer.EnsureAvailableSpace(buf.Length); buf.CopyTo(_inputBuffer.AvailableSpan); _inputBuffer.Commit(buf.Length); + + if (Interlocked.CompareExchange(ref _pendingInput, NoIOPending, ManagedIOPending) == Disposed) + { + _inputBuffer.Dispose(); + } } internal int BytesReadyForConnection => _outputBuffer.ActiveLength; internal byte[]? ReadPendingWrites() { - if (_outputBuffer.ActiveLength == 0) + if (Interlocked.Exchange(ref _pendingOutput, ManagedIOPending) == Disposed) { - return null; + throw new ObjectDisposedException(nameof(SafeDeleteSslContext)); } - byte[] buffer = _outputBuffer.ActiveSpan.ToArray(); - _outputBuffer.Discard(_outputBuffer.ActiveLength); + byte[]? buffer = null; + if (_outputBuffer.ActiveLength != 0) + { + buffer = _outputBuffer.ActiveSpan.ToArray(); + _outputBuffer.Discard(_outputBuffer.ActiveLength); + } + + if (Interlocked.CompareExchange(ref _pendingOutput, NoIOPending, ManagedIOPending) == Disposed) + { + _outputBuffer.Dispose(); + } return buffer; } @@ -228,11 +290,21 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) Debug.Assert(count >= 0); Debug.Assert(count <= buf.Length - offset); + if (Interlocked.Exchange(ref _pendingOutput, ManagedIOPending) == Disposed) + { + throw new ObjectDisposedException(nameof(SafeDeleteSslContext)); + } + int limit = Math.Min(count, _outputBuffer.ActiveLength); _outputBuffer.ActiveSpan.Slice(0, limit).CopyTo(new Span(buf, offset, limit)); _outputBuffer.Discard(limit); + if (Interlocked.CompareExchange(ref _pendingInput, NoIOPending, ManagedIOPending) == Disposed) + { + _outputBuffer.Dispose(); + } + return limit; }