blob: 7078da10175122a2ac3cd138649a9c651176ab39 [file] [log] [blame]
// 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 <cstddef>
#include <cstdint>
#include <limits>
#include <utility>
#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
#include "net/third_party/quiche/src/quic/core/qpack/qpack_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_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(QuicStringPiece /*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(QuicStringPiece /*name*/,
QuicStringPiece /*value*/) override {}
// Remove DecoderAndHandler from |*processing_decoders|.
void OnDecodingCompleted() override {
// Will delete |this|.
size_t result = processing_decoders_->erase(stream_id_);
CHECK_EQ(1u, result);
}
void OnDecodingErrorDetected(QuicStringPiece /*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) {
QuicFuzzedDataProvider 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