runtime: Violation of nullability constraint for certificate validation during TLS renegotiation in HTTP request

Description

When TLS is renegotiated in the middle of HTTP request the validation callback receives null for HttpRequestMessage which violates the nullability annotations of the callback.

Configuration

.NET 5.0.7 Windows 11 build 22000.51 (triggers the re-negotiation scenario) x64

Regression?

The error doesn’t happen on Windows 10 for the same request. That suggests it’s triggered by something in the TLS stack that changed in Windows 11.

Other information

Stack trace:

>	MailClient.dll!MailClient.Protocols.InteractionController.HttpClientCerificationValidationMessageHandler.ValidateCertificate(System.Net.Http.HttpRequestMessage message, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors) Line 498	C#
 	System.Net.Http.dll!System.Net.Http.ConnectHelper.EstablishSslConnectionAsync.AnonymousMethod__0(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)	Unknown
 	System.Net.Security.dll!System.Net.Security.SecureChannel.VerifyRemoteCertificate(System.Net.Security.RemoteCertificateValidationCallback remoteCertValidationCallback, ref System.Net.Security.ProtocolToken alertToken, out System.Net.Security.SslPolicyErrors sslPolicyErrors, out System.Security.Cryptography.X509Certificates.X509ChainStatusFlags chainStatus)	Unknown
 	System.Net.Security.dll!System.Net.Security.SslStream.CompleteHandshake(ref System.Net.Security.ProtocolToken alertToken, out System.Net.Security.SslPolicyErrors sslPolicyErrors, out System.Security.Cryptography.X509Certificates.X509ChainStatusFlags chainStatus)	Unknown
 	System.Net.Security.dll!System.Net.Security.SslStream.ForceAuthenticationAsync<System.Net.Security.AsyncReadWriteAdapter>(System.Net.Security.AsyncReadWriteAdapter adapter, bool receiveFirst, byte[] reAuthenticationData, bool isApm)	Unknown
 	System.Net.Security.dll!System.Net.Security.SslStream.ReplyOnReAuthenticationAsync<System.Net.Security.AsyncReadWriteAdapter>(System.Net.Security.AsyncReadWriteAdapter adapter, byte[] buffer)	Unknown
 	System.Net.Security.dll!System.Net.Security.SslStream.ReadAsyncInternal<System.Net.Security.AsyncReadWriteAdapter>(System.Net.Security.AsyncReadWriteAdapter adapter, System.Memory<byte> buffer)	Unknown
 	System.Net.Security.dll!System.Net.Security.SslStream.ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken)	Unknown
 	System.Net.Http.dll!System.Net.Http.HttpConnection.FillAsync(bool async)	Unknown
 	System.Net.Http.dll!System.Net.Http.HttpConnection.ReadNextResponseHeaderLineAsync(bool async, bool foldedHeadersAllowed)	Unknown
 	System.Net.Http.dll!System.Net.Http.HttpConnection.SendAsyncCore(System.Net.Http.HttpRequestMessage request, bool async, System.Threading.CancellationToken cancellationToken)	Unknown
 	[Resuming Async Method]	
 	System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Net.Http.HttpResponseMessage>.AsyncStateMachineBox<System.Net.Http.HttpConnection.<SendAsyncCore>d__56>.ExecutionContextCallback(object s)	Unknown
 	System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	Unknown
 	System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Net.Http.HttpResponseMessage>.AsyncStateMachineBox<System.Net.Http.HttpConnection.<SendAsyncCore>d__56>.MoveNext(System.Threading.Thread threadPoolThread)	Unknown
 	System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.__Canon>.AsyncStateMachineBox<System.Net.Http.HttpConnection.<SendAsyncCore>d__56>.MoveNext()	Unknown
 	System.Private.CoreLib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__12_0(System.Action innerContinuation, System.Threading.Tasks.Task innerTask)	Unknown
 	System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke()	Unknown
 	System.Private.CoreLib.dll!System.Threading.Tasks.AwaitTaskContinuation.System.Threading.IThreadPoolWorkItem.Execute()	Unknown
 	System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()	Unknown
 	System.Private.CoreLib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()	Unknown
 	[Async Call Stack]	
 	[Async] System.Net.Http.dll!System.Net.Http.AuthenticationHelper.SendWithNtAuthAsync(System.Net.Http.HttpRequestMessage request, System.Uri authUri, bool async, System.Net.ICredentials credentials, bool isProxyAuth, System.Net.Http.HttpConnection connection, System.Net.Http.HttpConnectionPool connectionPool, System.Threading.CancellationToken cancellationToken)	Unknown
 	[Async] System.Net.Http.dll!System.Net.Http.HttpConnectionPool.SendWithRetryAsync(System.Net.Http.HttpRequestMessage request, bool async, bool doRequestAuth, System.Threading.CancellationToken cancellationToken)	Unknown
 	[Async] System.Net.Http.dll!System.Net.Http.AuthenticationHelper.SendWithAuthAsync(System.Net.Http.HttpRequestMessage request, System.Uri authUri, bool async, System.Net.ICredentials credentials, bool preAuthenticate, bool isProxyAuth, bool doRequestAuth, System.Net.Http.HttpConnectionPool pool, System.Threading.CancellationToken cancellationToken)	Unknown
 	[Async] System.Net.Http.dll!System.Net.Http.RedirectHandler.SendAsync(System.Net.Http.HttpRequestMessage request, bool async, System.Threading.CancellationToken cancellationToken)	Unknown
 	[Async] System.Net.Http.dll!System.Net.Http.DecompressionHandler.SendAsync(System.Net.Http.HttpRequestMessage request, bool async, System.Threading.CancellationToken cancellationToken)	Unknown
 	[Async] MailClient.dll!MailClient.Protocols.InteractionController.HttpClientCerificationValidationMessageHandler.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Line 515	C#

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 18 (18 by maintainers)

Most upvoted comments

I think this is actually quite important to backport to 5.0. The conditions for hitting it are the following:

  • Using HttpClient to connect to HTTPS endpoints on Windows machine
  • Registering custom ServerCertificateCustomValidationCallback
  • Having TLS 1.3 enabled in the system which is default on Windows 11 and some insider builds at least

We started hitting it on day one of Windows 11 being pushed to Windows Insiders for a released version of our application. I suppose that others will start hitting it too once TLS 1.3 becomes more common. Note that .NET 5 enables TLS 1.3 support by default, it would be less of an issue if it didn’t already do that.

correct. I’m proposing to fix 5.0 to match Framework. (and 6.0)

For the sake of completeness I retested it on .NET 6 Preview 5 and it does behave correctly.

yes it should. It is also most likely Windows specific. With https://github.com/dotnet/runtime/pull/43123 we should verify the callback is called only once (at least from SslStream) but obviously we don’t in some cases.