Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Fix app crash when using non-native HttpClientHandler #69565

Conversation

simonrozsival
Copy link
Member

@simonrozsival simonrozsival commented May 19, 2022

HTTP requests done with HttpClient + HttpClientHandler caused Android apps to crash.

For example, a simple HTTP request using the default HttpClient will throw an exception with the following stack trace:

var client = new HttpClient();
try {
    var response = await client.GetAsync("https://microsoft.com");
    Console.WriteLine(response);
} catch (Exception e) {
    Console.WriteLine(e);
}
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
  ---> System.NullReferenceException: Object reference not set to an instance of an object
    at System.Net.CertificateValidationPal.GetRemoteCertificate(SafeDeleteContext securityContext, Boolean retrieveChainCertificates, X509Chain& chain)
    at System.Net.CertificateValidationPal.GetRemoteCertificate(SafeDeleteContext securityContext)
    at System.Net.Security.SslStream.SelectClientCertificate(Boolean& sessionRestartAttempt)
    at System.Net.Security.SslStream.AcquireClientCredentials(Byte[]& thumbPrint)
    at System.Net.Security.SslStream.GenerateToken(ReadOnlySpan`1 inputBuffer, Byte[]& output)
    at System.Net.Security.SslStream.NextMessage(ReadOnlySpan`1 incomingBuffer)
    at System.Net.Security.SslStream.<ForceAuthenticationAsync>d__144`1[[System.Net.Security.AsyncReadWriteAdapter, System.Net.Security, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
    --- End of inner exception stack trace ---
    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
    at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1[[System.Net.Http.HttpConnection, System.Net.Http, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
    at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
    at Program.<Main>$(String[] args) in /home/simon/dotnet/runtime/src/mono/sample/Android/Program.cs:line 12

There is a missing null check in CertificateValidationpal.Android.cs which was removed recently (https://github.com/dotnet/runtime/pull/68188/files#diff-5f4278661dbb1331a13f14f1bf94b3a67e7474e8e4c32a21a99860f966ab02aeL66-L68).

Possibly also related to #69557

@ghost
Copy link

ghost commented May 19, 2022

Tagging subscribers to 'arch-android': @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.

Issue Details

HTTP requests done with HttpClient + HttpClientHandler caused Android apps to crash. For example the test added in this PR would crash with this stack trace:

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
  ---> System.NullReferenceException: Object reference not set to an instance of an object
    at System.Net.CertificateValidationPal.GetRemoteCertificate(SafeDeleteContext securityContext, Boolean retrieveChainCertificates, X509Chain& chain)
    at System.Net.CertificateValidationPal.GetRemoteCertificate(SafeDeleteContext securityContext)
    at System.Net.Security.SslStream.SelectClientCertificate(Boolean& sessionRestartAttempt)
    at System.Net.Security.SslStream.AcquireClientCredentials(Byte[]& thumbPrint)
    at System.Net.Security.SslStream.GenerateToken(ReadOnlySpan`1 inputBuffer, Byte[]& output)
    at System.Net.Security.SslStream.NextMessage(ReadOnlySpan`1 incomingBuffer)
    at System.Net.Security.SslStream.<ForceAuthenticationAsync>d__144`1[[System.Net.Security.AsyncReadWriteAdapter, System.Net.Security, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
    --- End of inner exception stack trace ---
    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
    at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1[[System.Net.Http.HttpConnection, System.Net.Http, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
    at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
    at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
    at Program.<Main>$(String[] args) in /home/simon/dotnet/runtime/src/mono/sample/Android/Program.cs:line 12

There is a missing null check in CertificateValidationpal.Android.cs which was removed recently (https://github.com/dotnet/runtime/pull/68188/files#diff-5f4278661dbb1331a13f14f1bf94b3a67e7474e8e4c32a21a99860f966ab02aeL66-L68).

Possibly also related to #69557

Author: simonrozsival
Assignees: -
Labels:

os-android

Milestone: -

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@@ -46,7 +46,7 @@ internal static partial class CertificateValidationPal
bool retrieveChainCertificates,
ref X509Chain? chain)
{
SafeSslHandle sslContext = ((SafeDeleteSslContext)securityContext).SslContext;
SafeSslHandle? sslContext = ((SafeDeleteSslContext)securityContext)?.SslContext;
Copy link
Member

@akoeplinger akoeplinger May 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shouldn't happen given that securityContext isn't marked as nullable, which suggests to me something higher up the call chain is doing something unexpected.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The underlaying handle is nullable.

        private SafeFreeCredentials? _credentialsHandle;
        private SafeDeleteSslContext? _securityContext;

But is seems like it is like this for long time

remoteCert = CertificateValidationPal.GetRemoteCertificate(_securityContext!);

Making the parameter nullable make sense to me. I think it will be null when we try to select client certificate before the handshake. cc: @rzikm

Copy link
Member Author

@simonrozsival simonrozsival May 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok thanks, that sounds like indeed we're expecting a null securityContext here and should make it consistent across platforms.

@steveisok steveisok requested a review from wfurt May 19, 2022 17:15
@wfurt
Copy link
Member

wfurt commented May 19, 2022

On a side note, #69527 removes the dual SafeHandle wrapping for Linux. We could do something similar for Android in future.

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival
Copy link
Member Author

/azp run outerloop-mono

@azure-pipelines
Copy link

No pipelines are associated with this pull request.

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival
Copy link
Member Author

/azp run runtime-libraries-mono outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival
Copy link
Member Author

I re-enabled the functional tests for System.Net.Http on Android and disabled all the tests that are failing. The bug that's fixed in this PR fixed several dozen outerloop tests.

@dotnet dotnet deleted a comment from azure-pipelines bot May 27, 2022
@simonrozsival
Copy link
Member Author

/azp run runtime-libraries-mono outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival
Copy link
Member Author

I think this PR is now ready:

  • the failing runtime test has something to do with Tar on Linux
  • the failing runtime-extra-platforms tests are unrelated to this PR (Browser, iOS, tvOS, Linux)
  • the failing runtime-libraries-mono outerloop tests are unrelated to this PR
    • the only Android-related leg doesn't have any test failures related to System.Net.Http

@steveisok steveisok self-requested a review May 31, 2022 14:38
@simonrozsival simonrozsival merged commit aafe4d4 into dotnet:main Jun 6, 2022
@simonrozsival simonrozsival deleted the fix-android-certificate-validation-pal-null-reference-exception branch June 6, 2022 09:08
@ghost ghost locked as resolved and limited conversation to collaborators Jul 6, 2022
@karelz karelz added this to the 7.0.0 milestone Jul 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants