| // Copyright 2016 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 <cstddef> | |
| #include <cstdint> | |
| #include <limits> | |
| #include <utility> | |
| #include "absl/strings/string_view.h" | |
| #include "quic/core/qpack/qpack_decoder.h" | |
| #include "quic/core/quic_error_codes.h" | |
| #include "quic/test_tools/qpack/qpack_decoder_test_utils.h" | |
| #include "quic/test_tools/qpack/qpack_test_utils.h" | |
| namespace quic { | |
| namespace test { | |
| struct DecoderAndHandler { | |
| std::unique_ptr<QpackProgressiveDecoder> decoder; | |
| std::unique_ptr<QpackProgressiveDecoder::HeadersHandlerInterface> handler; | |
| }; | |
| using DecoderAndHandlerMap = std::map<QuicStreamId, DecoderAndHandler>; | |
| // Class that sets externally owned |error_detected| to true | |
| // on encoder stream error. | |
| class ErrorDelegate : public QpackDecoder::EncoderStreamErrorDelegate { | |
| public: | |
| ErrorDelegate(bool* error_detected) : error_detected_(error_detected) {} | |
| ~ErrorDelegate() override = default; | |
| void OnEncoderStreamError(QuicErrorCode /*error_code*/, | |
| absl::string_view /*error_message*/) override { | |
| *error_detected_ = true; | |
| } | |
| private: | |
| bool* const error_detected_; | |
| }; | |
| // Class that destroys DecoderAndHandler when decoding completes, and sets | |
| // externally owned |error_detected| to true on encoder stream error. | |
| class HeadersHandler : public QpackProgressiveDecoder::HeadersHandlerInterface { | |
| public: | |
| HeadersHandler(QuicStreamId stream_id, | |
| DecoderAndHandlerMap* processing_decoders, | |
| bool* error_detected) | |
| : stream_id_(stream_id), | |
| processing_decoders_(processing_decoders), | |
| error_detected_(error_detected) {} | |
| ~HeadersHandler() override = default; | |
| void OnHeaderDecoded(absl::string_view /*name*/, | |
| absl::string_view /*value*/) override {} | |
| // Remove DecoderAndHandler from |*processing_decoders|. | |
| void OnDecodingCompleted() override { | |
| // Will delete |this|. | |
| size_t result = processing_decoders_->erase(stream_id_); | |
| QUICHE_CHECK_EQ(1u, result); | |
| } | |
| void OnDecodingErrorDetected(QuicErrorCode /*error_code*/, | |
| absl::string_view /*error_message*/) override { | |
| *error_detected_ = true; | |
| } | |
| private: | |
| const QuicStreamId stream_id_; | |
| DecoderAndHandlerMap* const processing_decoders_; | |
| bool* const error_detected_; | |
| }; | |
| // This fuzzer exercises QpackDecoder. It should be able to cover all possible | |
| // code paths. There is no point in encoding QpackDecoder's output to turn this | |
| // into a roundtrip test, because the same header list can be encoded in many | |
| // different ways, so the output could not be expected to match the original | |
| // input. | |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { | |
| FuzzedDataProvider provider(data, size); | |
| // Maximum 256 byte dynamic table. Such a small size helps test draining | |
| // entries and eviction. | |
| const uint64_t maximum_dynamic_table_capacity = | |
| provider.ConsumeIntegral<uint8_t>(); | |
| // Maximum 256 blocked streams. | |
| const uint64_t maximum_blocked_streams = provider.ConsumeIntegral<uint8_t>(); | |
| // |error_detected| will be set to true if an error is encountered either in a | |
| // header block or on the encoder stream. | |
| bool error_detected = false; | |
| ErrorDelegate encoder_stream_error_delegate(&error_detected); | |
| QpackDecoder decoder(maximum_dynamic_table_capacity, maximum_blocked_streams, | |
| &encoder_stream_error_delegate); | |
| NoopQpackStreamSenderDelegate decoder_stream_sender_delegate; | |
| decoder.set_qpack_stream_sender_delegate(&decoder_stream_sender_delegate); | |
| // Decoders still reading the header block, with corresponding handlers. | |
| DecoderAndHandlerMap reading_decoders; | |
| // Decoders still processing the completely read header block, | |
| // with corresponding handlers. | |
| DecoderAndHandlerMap processing_decoders; | |
| // Maximum 256 data fragments to limit runtime and memory usage. | |
| auto fragment_count = provider.ConsumeIntegral<uint8_t>(); | |
| while (fragment_count > 0 && !error_detected && | |
| provider.remaining_bytes() > 0) { | |
| --fragment_count; | |
| switch (provider.ConsumeIntegralInRange<uint8_t>(0, 3)) { | |
| // Feed encoder stream data to QpackDecoder. | |
| case 0: { | |
| size_t fragment_size = provider.ConsumeIntegral<uint8_t>(); | |
| std::string data = provider.ConsumeRandomLengthString(fragment_size); | |
| decoder.encoder_stream_receiver()->Decode(data); | |
| continue; | |
| } | |
| // Create new progressive decoder. | |
| case 1: { | |
| QuicStreamId stream_id = provider.ConsumeIntegral<uint8_t>(); | |
| if (reading_decoders.find(stream_id) != reading_decoders.end() || | |
| processing_decoders.find(stream_id) != processing_decoders.end()) { | |
| continue; | |
| } | |
| DecoderAndHandler decoder_and_handler; | |
| decoder_and_handler.handler = std::make_unique<HeadersHandler>( | |
| stream_id, &processing_decoders, &error_detected); | |
| decoder_and_handler.decoder = decoder.CreateProgressiveDecoder( | |
| stream_id, decoder_and_handler.handler.get()); | |
| reading_decoders.insert({stream_id, std::move(decoder_and_handler)}); | |
| continue; | |
| } | |
| // Feed header block data to existing decoder. | |
| case 2: { | |
| if (reading_decoders.empty()) { | |
| continue; | |
| } | |
| auto it = reading_decoders.begin(); | |
| auto distance = provider.ConsumeIntegralInRange<uint8_t>( | |
| 0, reading_decoders.size() - 1); | |
| std::advance(it, distance); | |
| size_t fragment_size = provider.ConsumeIntegral<uint8_t>(); | |
| std::string data = provider.ConsumeRandomLengthString(fragment_size); | |
| it->second.decoder->Decode(data); | |
| continue; | |
| } | |
| // End header block. | |
| case 3: { | |
| if (reading_decoders.empty()) { | |
| continue; | |
| } | |
| auto it = reading_decoders.begin(); | |
| auto distance = provider.ConsumeIntegralInRange<uint8_t>( | |
| 0, reading_decoders.size() - 1); | |
| std::advance(it, distance); | |
| QpackProgressiveDecoder* decoder = it->second.decoder.get(); | |
| // Move DecoderAndHandler to |reading_decoders| first, because | |
| // EndHeaderBlock() might synchronously call OnDecodingCompleted(). | |
| QuicStreamId stream_id = it->first; | |
| processing_decoders.insert({stream_id, std::move(it->second)}); | |
| reading_decoders.erase(it); | |
| decoder->EndHeaderBlock(); | |
| continue; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| } // namespace test | |
| } // namespace quic |