-
Notifications
You must be signed in to change notification settings - Fork 653
Description
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:
msquic/src/test/lib/HandshakeTest.cpp
Lines 887 to 894 in 3ef1931
| 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
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.