Guard against integer overflow when decoding QUIC receive timestamp deltas This makes sure that the decoded delta itself does not overflow; we might want to limit the size of the deltas after that (so that the timestamp math does not overflow) too, but that's an issue for a separate CL. PiperOrigin-RevId: 983989890
diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc index 5884abe..4ff9171 100644 --- a/quiche/quic/core/quic_framer.cc +++ b/quiche/quic/core/quic_framer.cc
@@ -3898,9 +3898,18 @@ set_detailed_error("Unable to read receive timestamp delta."); return false; } + // The IETF draft does not allow exponents above 20; this ensures that the + // bitshifts below are always valid. + QUICHE_DCHECK_LE(local_receive_timestamps_exponent_, 20u); // The first timestamp delta is relative to framer creation time; whereas // subsequent deltas are relative to the previous delta in decreasing // packet order. + if (timestamp_delta > + (static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) >> + local_receive_timestamps_exponent_)) { + set_detailed_error("Receive timestamp delta too high."); + return false; + } timestamp_delta = timestamp_delta << local_receive_timestamps_exponent_; if (i == 0 && j == 0) { last_timestamp_ = QuicTime::Delta::FromMicroseconds(timestamp_delta);
diff --git a/quiche/quic/core/quic_framer_test.cc b/quiche/quic/core/quic_framer_test.cc index 0ceec4e..4bafd02 100644 --- a/quiche/quic/core/quic_framer_test.cc +++ b/quiche/quic/core/quic_framer_test.cc
@@ -4110,6 +4110,63 @@ "Receive timestamp count too high.")); } +TEST_P(QuicFramerTest, AckFrameReceiveTimestampDeltaShiftOverflow) { + if (!VersionIsIetfQuic(framer_.transport_version())) { + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet_ietf = { + // 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_ACK_RECEIVE_TIMESTAMPS frame) + {"", + { 0x83, 0x17, 0x83, 0x07 }}, + // largest acked + {"Unable to read largest acked.", + { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 + // Zero delta time. + {"Unable to read ack delay time.", + { kVarInt62OneByte + 0x00 }}, + // number of additional ack blocks + {"Unable to read ack block count.", + { kVarInt62OneByte + 0x00 }}, + // first ack block length. + {"Unable to read first ack block length.", + { kVarInt62OneByte + 0x00 }}, // 1st block length = 1 + + // Receive Timestamps. + { "Unable to read receive timestamp range count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp delta largest acked.", + { kVarInt62OneByte + 0x00 }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x01 }}, + // Max 62-bit varint (0x3fffffffffffffff), which overflows when shifted + // by exponent >= 2. + { "Unable to read receive timestamp delta.", + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet_ietf)); + + framer_.set_local_receive_timestamps_exponent(3); + framer_.set_local_max_receive_timestamps_per_ack(1000); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_TRUE(absl::StartsWith(framer_.detailed_error(), + "Receive timestamp delta too high.")); +} + TEST_P(QuicFramerTest, AckFrameTimeStampDeltaTooHigh) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off