Skip to content

ConnectionShutdown with pending (async) CertificateValidation sends Application CONNECTION_CLOSE #4166

@rzikm

Description

@rzikm

Describe the bug

When QUIC_STATUS_PENDING is returned when handling the QUIC_CONNECTION_EVENT_PEER_CERTIFICATE_RECEIVED event and application layer decides to shutdown the connection, MsQuic will end up sending the application-level CONNECTION_CLOSE frame. However, since the handshake is not complete at this point, the peer may not process this frame and may not reply to it, which makes server wait until the handshake timeouts.

Note that the above may happen even if application calls ConnectionCertificateValidationComplete before ConnectionShutdown, because ConnectionShutdown is scheduled to the front of the operation queue and will be processed first.

Affected OS

  • Windows
  • Linux
  • macOS
  • Other (specify below)

Additional OS information

No response

MsQuic version

main

Steps taken to reproduce bug

See description.

Quick repro can be achieved in tests by following changes:

if (AsyncValidation) {
CxPlatSleep(1000);
TEST_QUIC_SUCCEEDED(Client.SetCustomValidationResult(AcceptCert));
}
if (!Client.WaitForConnectionComplete()) {
return;
}

                if (AsyncValidation) {
                    CxPlatSleep(1000);
                    // TEST_QUIC_SUCCEEDED(Client.SetCustomValidationResult(AcceptCert));
                    Client.Shutdown(QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, QUIC_TEST_NO_ERROR);
                }

                // if (!Client.WaitForConnectionComplete()) {
                if (!Client.WaitForShutdownComplete()) {
                    return;
                }

and running the Handshake/WithHandshakeArgs5.CustomServerCertificateValidation/1 test case.

Expected behavior

Ideally, if ConnectionCertificateValidationComplete was called first (with Result=FALSE), I would expect the CONNECTION_CLOSE sent by MsQuic to reflect that.

At the very least, MsQuic should not attempt sending application CONNECTION_CLOSE before the handshake ends.

Actual outcome

Server hangs until configured handshake timeout.

Additional details

Possible fix lies in tweaking the condition in

msquic/src/core/connection.c

Lines 1443 to 1462 in f62a363

if (!ClosedRemotely) {
if ((Flags & QUIC_CLOSE_APPLICATION) &&
Connection->Crypto.TlsState.WriteKey < QUIC_PACKET_KEY_1_RTT) {
//
// Application close can only happen if we are using 1-RTT keys.
// Otherwise we have to send "user_canceled" TLS error code as a
// connection close. Overwrite all application provided parameters.
//
Flags &= ~QUIC_CLOSE_APPLICATION;
ErrorCode = QUIC_ERROR_CRYPTO_USER_CANCELED;
RemoteReasonPhrase = NULL;
RemoteReasonPhraseLength = 0;
QuicTraceLogConnInfo(
CloseUserCanceled,
Connection,
"Connection close using user canceled error");
}
}

Similarly to #4132, the condition Connection->Crypto.TlsState.WriteKey < QUIC_PACKET_KEY_1_RTT may be false while still in handshake and server is not allowed to process such 1RTT packets.

However, this suggestion does not fix the "race" when ConnectionCertificateValidationComplete and ConnectionShutdown are called in quick succession.

Metadata

Metadata

Assignees

Labels

Area: CoreRelated to the shared, core protocol logicPartner: .NETBy or For the .NET team

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions