Report QUIC_BUG if application tries to write data before encryption is established. gfe-relnote: no behavior change in production. not protected. PiperOrigin-RevId: 300639905 Change-Id: I0d6a6c6a73f95ded04da01a8ec8a0920f877399a
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc index dc33775..06912c3 100644 --- a/quic/core/http/quic_spdy_client_session_test.cc +++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -14,6 +14,7 @@ #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" #include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" @@ -231,11 +232,10 @@ EXPECT_TRUE(session_->CreateOutgoingBidirectionalStream() == nullptr); // Verify that no data may be send on existing streams. char data[] = "hello world"; - QuicConsumedData consumed = + EXPECT_QUIC_BUG( session_->WritevData(stream->id(), QUICHE_ARRAYSIZE(data), 0, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); - EXPECT_FALSE(consumed.fin_consumed); - EXPECT_EQ(0u, consumed.bytes_consumed); + NOT_RETRANSMISSION, QuicheNullOpt), + "Client: Try to send data of stream"); } TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) {
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index c0a1269..635b3c3 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1033,6 +1033,15 @@ return; } connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + if (connection_->version().HasHandshakeDone()) { + EXPECT_CALL(*connection_, SendControlFrame(_)); + } + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); session_.SendHttp3GoAway(); EXPECT_TRUE(session_.http3_goaway_sent()); @@ -1108,13 +1117,20 @@ } TEST_P(QuicSpdySessionTestServer, RstStreamBeforeHeadersDecompressed) { + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + EXPECT_CALL(*connection_, SendControlFrame(_)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); // Send two bytes of payload. QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, quiche::QuicheStringPiece("HT")); session_.OnStreamFrame(data1); EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams()); - EXPECT_CALL(*connection_, SendControlFrame(_)); if (!VersionHasIetfQuicFrames(transport_version())) { // For version99, OnStreamReset gets called because of the STOP_SENDING, // below. EXPECT the call there. @@ -1384,6 +1400,15 @@ TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingRstOutOfOrder) { + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); // Test that when we receive an out of order stream RST we correctly adjust // our connection level flow control receive window. // On close, the stream should mark as consumed all bytes between the highest @@ -1393,9 +1418,6 @@ const QuicStreamOffset kByteOffset = 1 + kInitialSessionFlowControlWindowForTest / 2; - EXPECT_CALL(*connection_, SendControlFrame(_)) - .Times(2) - .WillRepeatedly(Invoke(&ClearControlFrame)); if (!VersionHasIetfQuicFrames(transport_version())) { // For version99 the call to OnStreamReset happens as a result of receiving // the STOP_SENDING, so set up the EXPECT there. @@ -1450,6 +1472,15 @@ } TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingFinAfterRst) { + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); // Test that when we RST the stream (and tear down stream state), and then // receive a FIN from the peer, we correctly adjust our connection level flow // control receive window. @@ -1466,7 +1497,6 @@ // Reset our stream: this results in the stream being closed locally. TestStream* stream = session_.CreateOutgoingBidirectionalStream(); - EXPECT_CALL(*connection_, SendControlFrame(_)); EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); stream->Reset(QUIC_STREAM_CANCELLED); @@ -1489,6 +1519,15 @@ } TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingRstAfterRst) { + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); // Test that when we RST the stream (and tear down stream state), and then // receive a RST from the peer, we correctly adjust our connection level flow // control receive window. @@ -1580,6 +1619,15 @@ } TEST_P(QuicSpdySessionTestServer, FlowControlWithInvalidFinalOffset) { + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); // Test that if we receive a stream RST with a highest byte offset that // violates flow control, that we close the connection. const uint64_t kLargeOffset = kInitialSessionFlowControlWindowForTest + 1; @@ -1589,7 +1637,6 @@ // Check that stream frame + FIN results in connection close. TestStream* stream = session_.CreateOutgoingBidirectionalStream(); - EXPECT_CALL(*connection_, SendControlFrame(_)); EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); stream->Reset(QUIC_STREAM_CANCELLED); QuicStreamFrame frame(stream->id(), true, kLargeOffset, @@ -1818,6 +1865,16 @@ return; } + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); // Write headers with FIN set to close write side of stream. @@ -1871,14 +1928,22 @@ // IETF QUIC currently doesn't support PRIORITY. return; } + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); + TestHeadersStream* headers_stream; QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr); headers_stream = new TestHeadersStream(&session_); QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); // Make packet writer blocked so |headers_stream| will buffer its write data. - MockPacketWriter* writer = static_cast<MockPacketWriter*>( - QuicConnectionPeer::GetWriter(session_.connection())); EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true)); const QuicStreamId id = 4; @@ -2344,6 +2409,15 @@ if (!VersionUsesHttp3(transport_version())) { return; } + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); MockHttp3DebugVisitor debug_visitor; // Use an arbitrary stream id. QuicStreamId stream_id = @@ -2726,6 +2800,15 @@ } TEST_P(QuicSpdySessionTestServer, OnSetting) { + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); if (VersionUsesHttp3(transport_version())) { EXPECT_EQ(std::numeric_limits<size_t>::max(), session_.max_outbound_header_list_size());
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index ed1cde5..ad16ce3 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -708,6 +708,8 @@ !QuicUtils::IsCryptoStreamId(transport_version(), id)) { // Do not let streams write without encryption. The calling stream will end // up write blocked until OnCanWrite is next called. + QUIC_BUG << ENDPOINT << "Try to send data of stream " << id + << " before encryption is established."; return QuicConsumedData(0, false); }