| // Copyright (c) 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuzzer/FuzzedDataProvider.h> |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <string> |
| |
| #include "absl/base/macros.h" |
| #include "quiche/quic/core/crypto/null_decrypter.h" |
| #include "quiche/quic/core/crypto/null_encrypter.h" |
| #include "quiche/quic/core/quic_connection_id.h" |
| #include "quiche/quic/core/quic_constants.h" |
| #include "quiche/quic/core/quic_data_writer.h" |
| #include "quiche/quic/core/quic_framer.h" |
| #include "quiche/quic/core/quic_time.h" |
| #include "quiche/quic/core/quic_types.h" |
| #include "quiche/quic/core/quic_versions.h" |
| #include "quiche/quic/test_tools/quic_framer_peer.h" |
| #include "quiche/quic/test_tools/quic_test_utils.h" |
| |
| using quic::DiversificationNonce; |
| using quic::EncryptionLevel; |
| using quic::FirstSendingPacketNumber; |
| using quic::GetPacketHeaderSize; |
| using quic::kEthernetMTU; |
| using quic::kQuicDefaultConnectionIdLength; |
| using quic::NullDecrypter; |
| using quic::NullEncrypter; |
| using quic::PacketHeaderFormat; |
| using quic::ParsedQuicVersion; |
| using quic::ParsedQuicVersionVector; |
| using quic::Perspective; |
| using quic::QuicConnectionId; |
| using quic::QuicDataReader; |
| using quic::QuicDataWriter; |
| using quic::QuicEncryptedPacket; |
| using quic::QuicFramer; |
| using quic::QuicFramerVisitorInterface; |
| using quic::QuicLongHeaderType; |
| using quic::QuicPacketHeader; |
| using quic::QuicPacketNumber; |
| using quic::QuicTime; |
| using quic::QuicTransportVersion; |
| using quic::test::NoOpFramerVisitor; |
| using quic::test::QuicFramerPeer; |
| |
| PacketHeaderFormat ConsumePacketHeaderFormat(FuzzedDataProvider* provider, |
| ParsedQuicVersion version) { |
| if (!version.HasIetfInvariantHeader()) { |
| return quic::GOOGLE_QUIC_PACKET; |
| } |
| return provider->ConsumeBool() ? quic::IETF_QUIC_LONG_HEADER_PACKET |
| : quic::IETF_QUIC_SHORT_HEADER_PACKET; |
| } |
| |
| ParsedQuicVersion ConsumeParsedQuicVersion(FuzzedDataProvider* provider) { |
| // TODO(wub): Add support for v49+. |
| const QuicTransportVersion transport_versions[] = { |
| quic::QUIC_VERSION_43, |
| quic::QUIC_VERSION_46, |
| }; |
| |
| return ParsedQuicVersion( |
| quic::PROTOCOL_QUIC_CRYPTO, |
| transport_versions[provider->ConsumeIntegralInRange<uint8_t>( |
| 0, ABSL_ARRAYSIZE(transport_versions) - 1)]); |
| } |
| |
| // QuicSelfContainedPacketHeader is a QuicPacketHeader with built-in stroage for |
| // diversification nonce. |
| struct QuicSelfContainedPacketHeader : public QuicPacketHeader { |
| DiversificationNonce nonce_storage; |
| }; |
| |
| // Construct a random data packet header that 1) can be successfully serialized |
| // at sender, and 2) the serialzied buffer can pass the receiver framer's |
| // ProcessPublicHeader and DecryptPayload functions. |
| QuicSelfContainedPacketHeader ConsumeQuicPacketHeader( |
| FuzzedDataProvider* provider, Perspective receiver_perspective) { |
| QuicSelfContainedPacketHeader header; |
| |
| header.version = ConsumeParsedQuicVersion(provider); |
| |
| header.form = ConsumePacketHeaderFormat(provider, header.version); |
| |
| const std::string cid_bytes = |
| provider->ConsumeBytesAsString(kQuicDefaultConnectionIdLength); |
| if (receiver_perspective == Perspective::IS_SERVER) { |
| header.destination_connection_id = |
| QuicConnectionId(cid_bytes.c_str(), cid_bytes.size()); |
| header.destination_connection_id_included = quic::CONNECTION_ID_PRESENT; |
| header.source_connection_id_included = quic::CONNECTION_ID_ABSENT; |
| } else { |
| header.source_connection_id = |
| QuicConnectionId(cid_bytes.c_str(), cid_bytes.size()); |
| header.source_connection_id_included = quic::CONNECTION_ID_PRESENT; |
| header.destination_connection_id_included = quic::CONNECTION_ID_ABSENT; |
| } |
| |
| header.version_flag = receiver_perspective == Perspective::IS_SERVER; |
| header.reset_flag = false; |
| |
| header.packet_number = |
| QuicPacketNumber(provider->ConsumeIntegral<uint32_t>()); |
| if (header.packet_number < FirstSendingPacketNumber()) { |
| header.packet_number = FirstSendingPacketNumber(); |
| } |
| header.packet_number_length = quic::PACKET_4BYTE_PACKET_NUMBER; |
| |
| header.remaining_packet_length = 0; |
| |
| if (header.form != quic::GOOGLE_QUIC_PACKET && header.version_flag) { |
| header.long_packet_type = static_cast<QuicLongHeaderType>( |
| provider->ConsumeIntegralInRange<uint8_t>( |
| // INITIAL, ZERO_RTT_PROTECTED, or HANDSHAKE. |
| static_cast<uint8_t>(quic::INITIAL), |
| static_cast<uint8_t>(quic::HANDSHAKE))); |
| } else { |
| header.long_packet_type = quic::INVALID_PACKET_TYPE; |
| } |
| |
| if (header.form == quic::IETF_QUIC_LONG_HEADER_PACKET && |
| header.long_packet_type == quic::ZERO_RTT_PROTECTED && |
| receiver_perspective == Perspective::IS_CLIENT && |
| header.version.handshake_protocol == quic::PROTOCOL_QUIC_CRYPTO) { |
| for (size_t i = 0; i < header.nonce_storage.size(); ++i) { |
| header.nonce_storage[i] = provider->ConsumeIntegral<char>(); |
| } |
| header.nonce = &header.nonce_storage; |
| } else { |
| header.nonce = nullptr; |
| } |
| |
| return header; |
| } |
| |
| void SetupFramer(QuicFramer* framer, QuicFramerVisitorInterface* visitor) { |
| framer->set_visitor(visitor); |
| for (EncryptionLevel level : |
| {quic::ENCRYPTION_INITIAL, quic::ENCRYPTION_HANDSHAKE, |
| quic::ENCRYPTION_ZERO_RTT, quic::ENCRYPTION_FORWARD_SECURE}) { |
| framer->SetEncrypter( |
| level, std::make_unique<NullEncrypter>(framer->perspective())); |
| if (framer->version().KnowsWhichDecrypterToUse()) { |
| framer->InstallDecrypter( |
| level, std::make_unique<NullDecrypter>(framer->perspective())); |
| } |
| } |
| |
| if (!framer->version().KnowsWhichDecrypterToUse()) { |
| framer->SetDecrypter( |
| quic::ENCRYPTION_INITIAL, |
| std::make_unique<NullDecrypter>(framer->perspective())); |
| } |
| } |
| |
| class FuzzingFramerVisitor : public NoOpFramerVisitor { |
| public: |
| // Called after a successful ProcessPublicHeader. |
| bool OnUnauthenticatedPublicHeader( |
| const QuicPacketHeader& /*header*/) override { |
| ++process_public_header_success_count_; |
| return true; |
| } |
| |
| // Called after a successful DecryptPayload. |
| bool OnPacketHeader(const QuicPacketHeader& /*header*/) override { |
| ++decrypted_packet_count_; |
| return true; |
| } |
| |
| uint64_t process_public_header_success_count_ = 0; |
| uint64_t decrypted_packet_count_ = 0; |
| }; |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| FuzzedDataProvider data_provider(data, size); |
| |
| const QuicTime creation_time = |
| QuicTime::Zero() + QuicTime::Delta::FromMicroseconds( |
| data_provider.ConsumeIntegral<int32_t>()); |
| Perspective receiver_perspective = data_provider.ConsumeBool() |
| ? Perspective::IS_CLIENT |
| : Perspective::IS_SERVER; |
| Perspective sender_perspective = |
| (receiver_perspective == Perspective::IS_CLIENT) ? Perspective::IS_SERVER |
| : Perspective::IS_CLIENT; |
| |
| QuicSelfContainedPacketHeader header = |
| ConsumeQuicPacketHeader(&data_provider, receiver_perspective); |
| |
| NoOpFramerVisitor sender_framer_visitor; |
| ParsedQuicVersionVector framer_versions = {header.version}; |
| QuicFramer sender_framer(framer_versions, creation_time, sender_perspective, |
| kQuicDefaultConnectionIdLength); |
| SetupFramer(&sender_framer, &sender_framer_visitor); |
| |
| FuzzingFramerVisitor receiver_framer_visitor; |
| QuicFramer receiver_framer(framer_versions, creation_time, |
| receiver_perspective, |
| kQuicDefaultConnectionIdLength); |
| SetupFramer(&receiver_framer, &receiver_framer_visitor); |
| if (receiver_perspective == Perspective::IS_CLIENT) { |
| QuicFramerPeer::SetLastSerializedServerConnectionId( |
| &receiver_framer, header.source_connection_id); |
| } else { |
| QuicFramerPeer::SetLastSerializedClientConnectionId( |
| &receiver_framer, header.source_connection_id); |
| } |
| |
| std::array<char, kEthernetMTU> packet_buffer; |
| while (data_provider.remaining_bytes() > 16) { |
| const size_t last_remaining_bytes = data_provider.remaining_bytes(); |
| |
| // Get a randomized packet size. |
| uint16_t max_payload_size = static_cast<uint16_t>( |
| std::min<size_t>(data_provider.remaining_bytes(), 1350u)); |
| uint16_t min_payload_size = std::min<uint16_t>(16u, max_payload_size); |
| uint16_t payload_size = data_provider.ConsumeIntegralInRange<uint16_t>( |
| min_payload_size, max_payload_size); |
| |
| QUICHE_CHECK_NE(last_remaining_bytes, data_provider.remaining_bytes()) |
| << "Check fail to avoid an infinite loop. ConsumeIntegralInRange(" |
| << min_payload_size << ", " << max_payload_size |
| << ") did not consume any bytes. remaining_bytes:" |
| << last_remaining_bytes; |
| |
| std::vector<char> payload_buffer = |
| data_provider.ConsumeBytes<char>(payload_size); |
| QUICHE_CHECK_GE( |
| packet_buffer.size(), |
| GetPacketHeaderSize(sender_framer.transport_version(), header) + |
| payload_buffer.size()); |
| |
| // Serialize the null-encrypted packet into |packet_buffer|. |
| QuicDataWriter writer(packet_buffer.size(), packet_buffer.data()); |
| size_t length_field_offset = 0; |
| QUICHE_CHECK(sender_framer.AppendPacketHeader(header, &writer, |
| &length_field_offset)); |
| |
| QUICHE_CHECK( |
| writer.WriteBytes(payload_buffer.data(), payload_buffer.size())); |
| |
| EncryptionLevel encryption_level = |
| quic::test::HeaderToEncryptionLevel(header); |
| QUICHE_CHECK(sender_framer.WriteIetfLongHeaderLength( |
| header, &writer, length_field_offset, encryption_level)); |
| |
| size_t encrypted_length = sender_framer.EncryptInPlace( |
| encryption_level, header.packet_number, |
| GetStartOfEncryptedData(sender_framer.transport_version(), header), |
| writer.length(), packet_buffer.size(), packet_buffer.data()); |
| QUICHE_CHECK_NE(encrypted_length, 0u); |
| |
| // Use receiver's framer to process the packet. Ensure both |
| // ProcessPublicHeader and DecryptPayload were called and succeeded. |
| QuicEncryptedPacket packet(packet_buffer.data(), encrypted_length); |
| QuicDataReader reader(packet.data(), packet.length()); |
| |
| const uint64_t process_public_header_success_count = |
| receiver_framer_visitor.process_public_header_success_count_; |
| const uint64_t decrypted_packet_count = |
| receiver_framer_visitor.decrypted_packet_count_; |
| |
| receiver_framer.ProcessPacket(packet); |
| |
| QUICHE_DCHECK_EQ( |
| process_public_header_success_count + 1, |
| receiver_framer_visitor.process_public_header_success_count_) |
| << "ProcessPublicHeader failed. error:" |
| << QuicErrorCodeToString(receiver_framer.error()) |
| << ", error_detail:" << receiver_framer.detailed_error() |
| << ". header:" << header; |
| QUICHE_DCHECK_EQ(decrypted_packet_count + 1, |
| receiver_framer_visitor.decrypted_packet_count_) |
| << "Packet was not decrypted. error:" |
| << QuicErrorCodeToString(receiver_framer.error()) |
| << ", error_detail:" << receiver_framer.detailed_error() |
| << ". header:" << header; |
| } |
| return 0; |
| } |