Check new connection ID length before reading it This CL fixes a buffer overflow in the NEW_CONNECTION_ID read path, and adds sanity checks to prevent similar issues from reoccuring. The issue was found by clusterfuzz: https://bugs.chromium.org/p/chromium/issues/detail?id=943951#c4 gfe-relnote: trivial security fix when parsing invalid frame, not flag protected PiperOrigin-RevId: 239486794 Change-Id: I70b8e7b4adfd52afbbcb3308ba7dded0416c884e
diff --git a/quic/core/quic_data_reader.cc b/quic/core/quic_data_reader.cc index 4200329..0488fd3 100644 --- a/quic/core/quic_data_reader.cc +++ b/quic/core/quic_data_reader.cc
@@ -133,7 +133,11 @@ bool QuicDataReader::ReadConnectionId(QuicConnectionId* connection_id, uint8_t length) { - DCHECK_LE(length, kQuicMaxConnectionIdLength); + if (length > kQuicMaxConnectionIdLength) { + QUIC_BUG << "Attempted to read connection ID with length too high " + << static_cast<int>(length); + return false; + } if (length == 0) { connection_id->set_length(0); return true;
diff --git a/quic/core/quic_data_writer_test.cc b/quic/core/quic_data_writer_test.cc index aed14c1..031620d 100644 --- a/quic/core/quic_data_writer_test.cc +++ b/quic/core/quic_data_writer_test.cc
@@ -10,6 +10,7 @@ #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" @@ -38,7 +39,8 @@ class QuicDataWriterTest : public QuicTestWithParam<TestParams> {}; -INSTANTIATE_TEST_SUITE_P(QuicDataWriterTests, QuicDataWriterTest, +INSTANTIATE_TEST_SUITE_P(QuicDataWriterTests, + QuicDataWriterTest, ::testing::ValuesIn(GetTestParams())); TEST_P(QuicDataWriterTest, SanityCheckUFloat16Consts) { @@ -1097,6 +1099,21 @@ EXPECT_EQ(8, reader4.PeekVarInt62Length()); } +TEST_P(QuicDataWriterTest, InvalidConnectionIdLengthRead) { + static const uint8_t bad_connection_id_length = 19; + static_assert(bad_connection_id_length > kQuicMaxConnectionIdLength, + "bad lengths"); + char buffer[20] = {}; + QuicDataReader reader(buffer, 20); + QuicConnectionId connection_id; + bool ok; + EXPECT_QUIC_BUG( + ok = reader.ReadConnectionId(&connection_id, bad_connection_id_length), + QuicStrCat("Attempted to read connection ID with length too high ", + static_cast<int>(bad_connection_id_length))); + EXPECT_FALSE(ok); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index 333fe59..3e4f921 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -2579,6 +2579,9 @@ source_connection_id_length = scil; } + DCHECK_LE(destination_connection_id_length, kQuicMaxConnectionIdLength); + DCHECK_LE(source_connection_id_length, kQuicMaxConnectionIdLength); + // Read connection ID. if (!reader->ReadConnectionId(&header->destination_connection_id, destination_connection_id_length)) { @@ -5597,10 +5600,15 @@ return false; } + if (connection_id_length > kQuicMaxConnectionIdLength) { + set_detailed_error("New connection ID length too high."); + return false; + } + if (connection_id_length != kQuicDefaultConnectionIdLength && !QuicUtils::VariableLengthConnectionIdAllowedForVersion( transport_version())) { - set_detailed_error("Invalid new connection ID length."); + set_detailed_error("Invalid new connection ID length for version."); return false; }
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index 4604a08..ea362eb 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -11201,6 +11201,49 @@ CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA); } +// Verifies that parsing a NEW_CONNECTION_ID frame with a length above the +// specified maximum fails. +TEST_P(QuicFramerTest, InvalidLongNewConnectionIdFrame) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // The NEW_CONNECTION_ID frame is only for version 99. + return; + } + // clang-format off + PacketFragments packet99 = { + // 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_CONNECTION_ID frame) + {"", + {0x18}}, + // error code + {"Unable to read new connection ID frame sequence number.", + {kVarInt62OneByte + 0x11}}, + {"Unable to read new connection ID frame connection id length.", + {0x13}}, // connection ID length + {"Unable to read new connection ID frame connection id.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + 0xF0, 0xD2, 0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E, + 0x42, 0x33, 0x42}}, + {"Can not read new connection ID frame reset token.", + {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_INVALID_NEW_CONNECTION_ID_DATA, framer_.error()); + EXPECT_EQ("New connection ID length too high.", framer_.detailed_error()); +} + TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) { if (framer_.transport_version() != QUIC_VERSION_99) { // This frame is only for version 99.