@@ -54,10 +54,10 @@ private void CloseInternal()
54
54
55
55
// Ensure a Read or Auth operation is not in progress,
56
56
// block potential future read and auth operations since SslStream is disposing.
57
- // This leaves the _nestedRead = 1 and _nestedAuth = 1 , but that's ok, since
57
+ // This leaves the _nestedRead = 2 and _nestedAuth = 2 , but that's ok, since
58
58
// subsequent operations check the _exception sentinel first
59
- if ( Interlocked . Exchange ( ref _nestedRead , 1 ) == 0 &&
60
- Interlocked . Exchange ( ref _nestedAuth , 1 ) == 0 )
59
+ if ( Interlocked . Exchange ( ref _nestedRead , StreamDisposed ) == StreamNotInUse &&
60
+ Interlocked . Exchange ( ref _nestedAuth , StreamDisposed ) == StreamNotInUse )
61
61
{
62
62
_buffer . ReturnBuffer ( ) ;
63
63
}
@@ -162,19 +162,22 @@ private async Task ReplyOnReAuthenticationAsync<TIOAdapter>(byte[]? buffer, Canc
162
162
private async Task RenegotiateAsync < TIOAdapter > ( CancellationToken cancellationToken )
163
163
where TIOAdapter : IReadWriteAdapter
164
164
{
165
- if ( Interlocked . Exchange ( ref _nestedAuth , 1 ) == 1 )
165
+ if ( Interlocked . CompareExchange ( ref _nestedAuth , StreamInUse , StreamNotInUse ) != StreamNotInUse )
166
166
{
167
+ ObjectDisposedException . ThrowIf ( _nestedAuth == StreamDisposed , this ) ;
167
168
throw new InvalidOperationException ( SR . Format ( SR . net_io_invalidnestedcall , "authenticate" ) ) ;
168
169
}
169
170
170
- if ( Interlocked . Exchange ( ref _nestedRead , 1 ) == 1 )
171
+ if ( Interlocked . CompareExchange ( ref _nestedRead , StreamInUse , StreamNotInUse ) != StreamNotInUse )
171
172
{
173
+ ObjectDisposedException . ThrowIf ( _nestedRead == StreamDisposed , this ) ;
172
174
throw new NotSupportedException ( SR . Format ( SR . net_io_invalidnestedcall , "read" ) ) ;
173
175
}
174
176
175
- if ( Interlocked . Exchange ( ref _nestedWrite , 1 ) == 1 )
177
+ // Write is different since we do not do anything special in Dispose
178
+ if ( Interlocked . Exchange ( ref _nestedWrite , StreamInUse ) != StreamNotInUse )
176
179
{
177
- _nestedRead = 0 ;
180
+ _nestedRead = StreamNotInUse ;
178
181
throw new NotSupportedException ( SR . Format ( SR . net_io_invalidnestedcall , "write" ) ) ;
179
182
}
180
183
@@ -231,8 +234,8 @@ private async Task RenegotiateAsync<TIOAdapter>(CancellationToken cancellationTo
231
234
_buffer . ReturnBuffer ( ) ;
232
235
}
233
236
234
- _nestedRead = 0 ;
235
- _nestedWrite = 0 ;
237
+ _nestedRead = StreamNotInUse ;
238
+ _nestedWrite = StreamNotInUse ;
236
239
_isRenego = false ;
237
240
// We will not release _nestedAuth at this point to prevent another renegotiation attempt.
238
241
}
@@ -248,7 +251,7 @@ private async Task ForceAuthenticationAsync<TIOAdapter>(bool receiveFirst, byte[
248
251
if ( reAuthenticationData == null )
249
252
{
250
253
// prevent nesting only when authentication functions are called explicitly. e.g. handle renegotiation transparently.
251
- if ( Interlocked . Exchange ( ref _nestedAuth , 1 ) == 1 )
254
+ if ( Interlocked . Exchange ( ref _nestedAuth , StreamInUse ) == StreamInUse )
252
255
{
253
256
throw new InvalidOperationException ( SR . Format ( SR . net_io_invalidnestedcall , "authenticate" ) ) ;
254
257
}
@@ -335,7 +338,7 @@ private async Task ForceAuthenticationAsync<TIOAdapter>(bool receiveFirst, byte[
335
338
{
336
339
if ( reAuthenticationData == null )
337
340
{
338
- _nestedAuth = 0 ;
341
+ _nestedAuth = StreamNotInUse ;
339
342
_isRenego = false ;
340
343
}
341
344
}
@@ -500,7 +503,7 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError
500
503
{
501
504
ProcessHandshakeSuccess ( ) ;
502
505
503
- if ( _nestedAuth != 1 )
506
+ if ( _nestedAuth != StreamInUse )
504
507
{
505
508
if ( NetEventSource . Log . IsEnabled ( ) ) NetEventSource . Error ( this , $ "Ignoring unsolicited renegotiated certificate.") ;
506
509
// ignore certificates received outside of handshake or requested renegotiation.
@@ -769,13 +772,16 @@ private SecurityStatusPal DecryptData(int frameSize)
769
772
private async ValueTask < int > ReadAsyncInternal < TIOAdapter > ( Memory < byte > buffer , CancellationToken cancellationToken )
770
773
where TIOAdapter : IReadWriteAdapter
771
774
{
772
- if ( Interlocked . Exchange ( ref _nestedRead , 1 ) == 1 )
775
+ // Throw first if we already have exception.
776
+ // Check for disposal is not atomic so we will check again below.
777
+ ThrowIfExceptionalOrNotAuthenticated ( ) ;
778
+
779
+ if ( Interlocked . CompareExchange ( ref _nestedRead , StreamInUse , StreamNotInUse ) != StreamNotInUse )
773
780
{
781
+ ObjectDisposedException . ThrowIf ( _nestedRead == StreamDisposed , this ) ;
774
782
throw new NotSupportedException ( SR . Format ( SR . net_io_invalidnestedcall , "read" ) ) ;
775
783
}
776
784
777
- ThrowIfExceptionalOrNotAuthenticated ( ) ;
778
-
779
785
try
780
786
{
781
787
int processedLength = 0 ;
@@ -910,7 +916,7 @@ private async ValueTask<int> ReadAsyncInternal<TIOAdapter>(Memory<byte> buffer,
910
916
finally
911
917
{
912
918
ReturnReadBufferIfEmpty ( ) ;
913
- _nestedRead = 0 ;
919
+ _nestedRead = StreamNotInUse ;
914
920
}
915
921
}
916
922
@@ -925,7 +931,7 @@ private async ValueTask WriteAsyncInternal<TIOAdapter>(ReadOnlyMemory<byte> buff
925
931
return ;
926
932
}
927
933
928
- if ( Interlocked . Exchange ( ref _nestedWrite , 1 ) == 1 )
934
+ if ( Interlocked . Exchange ( ref _nestedWrite , StreamInUse ) == StreamInUse )
929
935
{
930
936
throw new NotSupportedException ( SR . Format ( SR . net_io_invalidnestedcall , "write" ) ) ;
931
937
}
@@ -948,7 +954,7 @@ private async ValueTask WriteAsyncInternal<TIOAdapter>(ReadOnlyMemory<byte> buff
948
954
}
949
955
finally
950
956
{
951
- _nestedWrite = 0 ;
957
+ _nestedWrite = StreamNotInUse ;
952
958
}
953
959
}
954
960
0 commit comments