1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/websockets/websocket_deflate_stream.h"
#include <fuzzer/FuzzedDataProvider.h>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/check.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_number_conversions.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_with_source.h"
#include "net/websockets/websocket_deflate_parameters.h"
#include "net/websockets/websocket_deflate_predictor.h"
#include "net/websockets/websocket_deflate_predictor_impl.h"
#include "net/websockets/websocket_extension.h"
#include "net/websockets/websocket_frame.h"
#include "net/websockets/websocket_stream.h"
namespace net {
namespace {
// If there are less random bytes left than MIN_BYTES_TO_CREATE_A_FRAME then
// CreateFrame() will always create an empty frame. Since the fuzzer can create
// the same empty frame with MIN_BYTES_TO_CREATE_A_FRAME bytes of input, save it
// from exploring a large space of ways to do the same thing.
constexpr size_t MIN_BYTES_TO_CREATE_A_FRAME = 3;
constexpr size_t BYTES_CONSUMED_BY_PARAMS = 2;
// If there are exactly BYTES_CONSUMED_BY_PARAMS + MIN_BYTES_TO_CREATE_A_FRAME
// bytes of input, then the fuzzer will test a single frame. In order to also
// test the case with zero frames, allow one less byte than this.
constexpr size_t MIN_USEFUL_SIZE =
BYTES_CONSUMED_BY_PARAMS + MIN_BYTES_TO_CREATE_A_FRAME - 1;
class WebSocketFuzzedStream final : public WebSocketStream {
public:
explicit WebSocketFuzzedStream(FuzzedDataProvider* fuzzed_data_provider)
: fuzzed_data_provider_(fuzzed_data_provider) {}
int ReadFrames(std::vector<std::unique_ptr<WebSocketFrame>>* frames,
CompletionOnceCallback callback) override {
if (fuzzed_data_provider_->remaining_bytes() < MIN_BYTES_TO_CREATE_A_FRAME)
return ERR_CONNECTION_CLOSED;
while (fuzzed_data_provider_->remaining_bytes() > 0)
frames->push_back(CreateFrame());
return OK;
}
int WriteFrames(std::vector<std::unique_ptr<WebSocketFrame>>* frames,
CompletionOnceCallback callback) override {
return ERR_FILE_NOT_FOUND;
}
void Close() override {}
std::string GetSubProtocol() const override { return std::string(); }
std::string GetExtensions() const override { return std::string(); }
const NetLogWithSource& GetNetLogWithSource() const override {
return net_log_;
}
private:
std::unique_ptr<WebSocketFrame> CreateFrame() {
WebSocketFrameHeader::OpCode opcode =
fuzzed_data_provider_
->ConsumeIntegralInRange<WebSocketFrameHeader::OpCode>(
WebSocketFrameHeader::kOpCodeContinuation,
WebSocketFrameHeader::kOpCodeControlUnusedF);
auto frame = std::make_unique<WebSocketFrame>(opcode);
// Bad news: ConsumeBool actually consumes a whole byte per call, so do
// something hacky to conserve precious bits.
uint8_t flags = fuzzed_data_provider_->ConsumeIntegral<uint8_t>();
frame->header.final = flags & 0x1;
frame->header.reserved1 = (flags >> 1) & 0x1;
frame->header.reserved2 = (flags >> 2) & 0x1;
frame->header.reserved3 = (flags >> 3) & 0x1;
frame->header.masked = (flags >> 4) & 0x1;
uint64_t payload_length =
fuzzed_data_provider_->ConsumeIntegralInRange(0, 64);
std::vector<char> payload =
fuzzed_data_provider_->ConsumeBytes<char>(payload_length);
auto buffer = base::MakeRefCounted<IOBufferWithSize>(payload.size());
buffer->span().copy_from(base::as_byte_span(payload));
buffers_.push_back(buffer);
frame->payload = buffer->span();
frame->header.payload_length = payload.size();
return frame;
}
std::vector<scoped_refptr<IOBufferWithSize>> buffers_;
raw_ptr<FuzzedDataProvider> fuzzed_data_provider_;
NetLogWithSource net_log_;
};
void WebSocketDeflateStreamFuzz(const uint8_t* data, size_t size) {
FuzzedDataProvider fuzzed_data_provider(data, size);
uint8_t flags = fuzzed_data_provider.ConsumeIntegral<uint8_t>();
bool server_no_context_takeover = flags & 0x1;
bool client_no_context_takeover = (flags >> 1) & 0x1;
uint8_t window_bits = fuzzed_data_provider.ConsumeIntegral<uint8_t>();
int server_max_window_bits = (window_bits & 0x7) + 8;
int client_max_window_bits = ((window_bits >> 3) & 0x7) + 8;
// WebSocketDeflateStream needs to be constructed on each call because it
// has state.
WebSocketExtension params("permessage-deflate");
if (server_no_context_takeover)
params.Add(WebSocketExtension::Parameter("server_no_context_takeover"));
if (client_no_context_takeover)
params.Add(WebSocketExtension::Parameter("client_no_context_takeover"));
params.Add(WebSocketExtension::Parameter(
"server_max_window_bits", base::NumberToString(server_max_window_bits)));
params.Add(WebSocketExtension::Parameter(
"client_max_window_bits", base::NumberToString(client_max_window_bits)));
std::string failure_message;
WebSocketDeflateParameters parameters;
DCHECK(parameters.Initialize(params, &failure_message)) << failure_message;
WebSocketDeflateStream deflate_stream(
std::make_unique<WebSocketFuzzedStream>(&fuzzed_data_provider),
parameters, std::make_unique<WebSocketDeflatePredictorImpl>());
std::vector<std::unique_ptr<net::WebSocketFrame>> frames;
deflate_stream.ReadFrames(&frames, CompletionOnceCallback());
}
} // namespace
} // namespace net
// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < net::MIN_USEFUL_SIZE)
return 0;
net::WebSocketDeflateStreamFuzz(data, size);
return 0;
}
|