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