Repository: dotnet/runtime
File: src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs (C#)

Code before:

                    // Read in the rest of the payload if we don't have it.
                    if (_internalBufferCount < payloadBytes)
                    {
                        await FillBufferAsync(adapter, payloadBytes).ConfigureAwait(false);
                    }

                    // Set _decrytpedBytesOffset/Count to the current frame we have (including header)
                    // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller.
                    _decryptedBytesOffset = _internalOffset;
                    _decryptedBytesCount = payloadBytes;

                    SecurityStatusPal status;
                    lock (_handshakeLock)
                    {
                        status = DecryptData();
                        if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
                        {
                            _handshakeWaiter = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                        }
                    }

                    // Treat the bytes we just decrypted as consumed
                    // Note, we won't do another buffer read until the decrypted bytes are processed
                    ConsumeBufferedBytes(payloadBytes);

                    if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
                    {
                        byte[]? extraBuffer = null;
                        if (_decryptedBytesCount != 0)
                        {
                            extraBuffer = new byte[_decryptedBytesCount];
                            Buffer.BlockCopy(_internalBuffer!, _decryptedBytesOffset, extraBuffer, 0, _decryptedBytesCount);

                            _decryptedBytesCount = 0;
                        }

                        if (NetEventSource.IsEnabled)
                            NetEventSource.Info(null, $"***Processing an error Status = {status}");

                        if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
                        {
                            if (!_sslAuthenticationOptions!.AllowRenegotiation)
                            {
                                if (NetEventSource.IsEnabled) NetEventSource.Fail(this, "Renegotiation was requested but it is disallowed");
                                throw new IOException(SR.net_ssl_io_renego);
                            }

                            await ReplyOnReAuthenticationAsync(adapter, extraBuffer).ConfigureAwait(false);
                            // Loop on read.
                            continue;

Review comment (security):
What happens if below we end up not calling ReplyOnReAuthenticationAsync, e.g. because AllowRenegotation is false?  That will leave _handshakeWaiter as non-null, but no one will ever complete it.  Is that going to cause problems?  Can we ensure that if this is set, it'll always be completed/removed?

Code after:
                        await FillBufferAsync(adapter, payloadBytes).ConfigureAwait(false);
                    }

                    // Set _decrytpedBytesOffset/Count to the current frame we have (including header)
                    // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller.
                    _decryptedBytesOffset = _internalOffset;
                    _decryptedBytesCount = payloadBytes;

                    SecurityStatusPal status;
                    lock (_handshakeLock)
                    {
                        status = DecryptData();
                        if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
                        {
                            // The status indicates that peer wants to renegotiate. (Windows only)
                            // In practice, there can be some other reasons too - like TLS1.3 session creation
                            // of alert handling. We need to pass the data to lsass and it is not safe to do parallel
                            // write any more as that can change TLS state and the EncryptData() can fail in strange ways.

                            // To handle this we call DecryptData() under lock and we create TCS waiter.
                            // EncryptData() checks that under same lock and if it exist it will not call low-level crypto.
                            // Instead it will wait synchronously or asynchronously and it will try again after the wait.
                            // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is over
                            // or if we bail to continue. If either one happen before EncryptData(), _handshakeWaiter will be set to null
                            // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData()
                            _handshakeWaiter = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                        }
                    }

                    // Treat the bytes we just decrypted as consumed
                    // Note, we won't do another buffer read until the decrypted bytes are processed
                    ConsumeBufferedBytes(payloadBytes);

                    if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
                    {
                        byte[]? extraBuffer = null;
                        if (_decryptedBytesCount != 0)
                        {
                            extraBuffer = new byte[_decryptedBytesCount];
                            Buffer.BlockCopy(_internalBuffer!, _decryptedBytesOffset, extraBuffer, 0, _decryptedBytesCount);

                            _decryptedBytesCount = 0;
                        }

                        if (NetEventSource.IsEnabled)
                            NetEventSource.Info(null, $"***Processing an error Status = {status}");

                        if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
                        {
                            if (!_sslAuthenticationOptions!.AllowRenegotiation)
                            {