| // 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 |