From 8110d42a44341d951de980ae6205e47c83b389c9 Mon Sep 17 00:00:00 2001 From: rootkiller6788 Date: Thu, 20 Aug 2026 02:40:22 +0800 Subject: [PATCH] Reject empty NEW_TOKEN frames with FRAME_ENCODING_ERROR RFC 9000 Section 19.7 requires the Token field in a NEW_TOKEN frame to be non-empty, and a client MUST treat receipt of a NEW_TOKEN frame with an empty Token field as a connection error of type FRAME_ENCODING_ERROR. quiche currently parses an empty token successfully and delivers it to the connection, leaving the connection open. Reject the empty token in the framer and raise QUIC_INVALID_FRAME_DATA, which maps to FRAME_ENCODING_ERROR on the wire. Update existing tests that used empty NEW_TOKEN frames as a convenience so they exercise non-empty tokens, and add a framer test covering the empty-token rejection. --- quiche/quic/core/quic_connection_test.cc | 8 +++--- quiche/quic/core/quic_framer.cc | 7 +++++ quiche/quic/core/quic_framer_test.cc | 33 ++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc index acb6de829..dd2d2a63c 100644 --- a/quiche/quic/core/quic_connection_test.cc +++ b/quiche/quic/core/quic_connection_test.cc @@ -14417,7 +14417,7 @@ TEST_P(QuicConnectionTest, NewTokenFrameInstigateAcks) { } EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - QuicNewTokenFrame* new_token = new QuicNewTokenFrame(); + QuicNewTokenFrame* new_token = new QuicNewTokenFrame(1, "new_token"); EXPECT_CALL(visitor_, OnNewTokenReceived(_)); ProcessFramePacket(QuicFrame(new_token)); @@ -14430,7 +14430,7 @@ TEST_P(QuicConnectionTest, ServerClosesConnectionOnNewTokenFrame) { return; } set_perspective(Perspective::IS_SERVER); - QuicNewTokenFrame* new_token = new QuicNewTokenFrame(); + QuicNewTokenFrame* new_token = new QuicNewTokenFrame(1, "new_token"); EXPECT_CALL(visitor_, OnNewTokenReceived(_)).Times(0); EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); EXPECT_CALL(visitor_, BeforeConnectionCloseSent()); @@ -16202,7 +16202,7 @@ TEST_P(QuicConnectionTest, AckElicitingFrames) { EXPECT_CALL(visitor_, OnStreamsBlockedFrame(_)); EXPECT_CALL(visitor_, OnStopSendingFrame(_)); EXPECT_CALL(visitor_, OnDatagramReceived("")); - EXPECT_CALL(visitor_, OnNewTokenReceived("")); + EXPECT_CALL(visitor_, OnNewTokenReceived("new_token")); SetClientConnectionId(TestConnectionId(12)); connection_.CreateConnectionIdManager(); @@ -16222,7 +16222,7 @@ TEST_P(QuicConnectionTest, AckElicitingFrames) { QuicStopSendingFrame stop_sending_frame; QuicPathResponseFrame path_response_frame; QuicDatagramFrame message_frame; - QuicNewTokenFrame new_token_frame; + QuicNewTokenFrame new_token_frame(1, "new_token"); QuicAckFrequencyFrame ack_frequency_frame; QuicResetStreamAtFrame reset_stream_at_frame; QuicBlockedFrame blocked_frame; diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc index 785b29bc6..dbeb942f5 100644 --- a/quiche/quic/core/quic_framer.cc +++ b/quiche/quic/core/quic_framer.cc @@ -3072,6 +3072,13 @@ bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, if (!ProcessNewTokenFrame(reader, &frame)) { return RaiseError(QUIC_INVALID_NEW_TOKEN); } + if (frame.token.empty()) { + // RFC 9000, Section 19.7: the token in a NEW_TOKEN frame MUST NOT + // be empty, and a client MUST treat receipt of an empty Token + // field as a connection error of type FRAME_ENCODING_ERROR. + set_detailed_error("New token frame has empty token."); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } QUIC_DVLOG(2) << ENDPOINT << "Processing IETF new token frame " << frame; if (!visitor_->OnNewTokenFrame(frame)) { diff --git a/quiche/quic/core/quic_framer_test.cc b/quiche/quic/core/quic_framer_test.cc index 0ceec4ec8..cddb8807e 100644 --- a/quiche/quic/core/quic_framer_test.cc +++ b/quiche/quic/core/quic_framer_test.cc @@ -11297,6 +11297,39 @@ TEST_P(QuicFramerTest, NewTokenFrame) { CheckFramingBoundaries(packet, QUIC_INVALID_NEW_TOKEN); } +TEST_P(QuicFramerTest, NewTokenFrameEmptyToken) { + if (!VersionIsIetfQuic(framer_.transport_version())) { + // This frame is only for IETF QUIC only. + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_NEW_TOKEN frame) + {"", + {0x07}}, + // Length (0 = empty token) + {"New token frame has empty token.", + {0x00}} + }; + // clang-format on + + std::unique_ptr encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsError(QUIC_INVALID_FRAME_DATA)); + EXPECT_EQ("New token frame has empty token.", framer_.detailed_error()); +} + TEST_P(QuicFramerTest, BuildNewTokenFramePacket) { if (!VersionIsIetfQuic(framer_.transport_version())) { // This frame is only for IETF QUIC only.