forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathurl_request_data_job_fuzzer.cc
178 lines (152 loc) · 6.21 KB
/
url_request_data_job_fuzzer.cc
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// 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 <memory>
#include <string>
#include "base/memory/ptr_util.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/fuzzed_data_provider.h"
#include "base/test/scoped_task_scheduler.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/http/http_request_headers.h"
#include "net/url_request/data_protocol_handler.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "net/url_request/url_request_test_util.h"
namespace {
const size_t kMaxLengthForFuzzedRange = 32;
} // namespace
// This class tests creating and reading to completion a URLRequest with fuzzed
// input. The fuzzer provides a data: URL and optionally generates custom Range
// headers. The amount of data read in each Read call is also fuzzed, as is
// the size of the IOBuffer to read data into.
class URLRequestDataJobFuzzerHarness : public net::URLRequest::Delegate {
public:
URLRequestDataJobFuzzerHarness()
: scoped_task_scheduler_(base::MessageLoop::current()),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
context_(true) {
job_factory_.SetProtocolHandler(
"data", base::MakeUnique<net::DataProtocolHandler>());
context_.set_job_factory(&job_factory_);
context_.Init();
}
static URLRequestDataJobFuzzerHarness* GetInstance() {
return base::Singleton<URLRequestDataJobFuzzerHarness>::get();
}
int CreateAndReadFromDataURLRequest(const uint8_t* data, size_t size) {
base::FuzzedDataProvider provider(data, size);
read_lengths_.clear();
// Allocate an IOBuffer with fuzzed size.
uint32_t buf_size = provider.ConsumeUint32InRange(1, 127); // 7 bits.
scoped_refptr<net::IOBuffer> buf(
new net::IOBuffer(static_cast<size_t>(buf_size)));
buf_.swap(buf);
// Generate a range header, and a bool determining whether to use it.
// Generate the header regardless of the bool value to keep the data URL and
// header in consistent byte addresses so the fuzzer doesn't have to work as
// hard.
bool use_range = provider.ConsumeBool();
std::string range(provider.ConsumeBytes(kMaxLengthForFuzzedRange));
// Generate a sequence of reads sufficient to read the entire data URL.
size_t simulated_bytes_read = 0;
while (simulated_bytes_read < provider.remaining_bytes()) {
size_t read_length = provider.ConsumeUint32InRange(1, buf_size);
read_lengths_.push_back(read_length);
simulated_bytes_read += read_length;
}
// The data URL is the rest of the fuzzed data with "data:" prepended, to
// ensure that if it's a URL, it's a data URL. If the URL is invalid just
// use a test variant, so the fuzzer has a chance to execute something.
std::string data_url_string =
std::string("data:") + provider.ConsumeRemainingBytes();
GURL data_url(data_url_string);
if (!data_url.is_valid())
data_url = GURL("data:text/html;charset=utf-8,<p>test</p>");
// Create a URLRequest with the given data URL and start reading
// from it.
std::unique_ptr<net::URLRequest> request =
context_.CreateRequest(data_url, net::DEFAULT_PRIORITY, this);
if (use_range) {
if (!net::HttpUtil::IsValidHeaderValue(range))
range = "bytes=3-";
request->SetExtraRequestHeaderByName("Range", range, true);
}
// Block the thread while the request is read.
base::RunLoop read_loop;
read_loop_ = &read_loop;
request->Start();
read_loop.Run();
read_loop_ = nullptr;
return 0;
}
void QuitLoop() {
DCHECK(read_loop_);
task_runner_->PostTask(FROM_HERE, read_loop_->QuitClosure());
}
void ReadFromRequest(net::URLRequest* request) {
int bytes_read = 0;
do {
// If possible, pop the next read size. If none exists, then this should
// be the last call to Read.
bool using_populated_read = read_lengths_.size() > 0;
size_t read_size = 1;
if (using_populated_read) {
read_size = read_lengths_.back();
read_lengths_.pop_back();
}
bytes_read = request->Read(buf_.get(), read_size);
} while (bytes_read > 0);
if (bytes_read != net::ERR_IO_PENDING)
QuitLoop();
}
// net::URLRequest::Delegate:
void OnReceivedRedirect(net::URLRequest* request,
const net::RedirectInfo& redirect_info,
bool* defer_redirect) override {}
void OnAuthRequired(net::URLRequest* request,
net::AuthChallengeInfo* auth_info) override {}
void OnCertificateRequested(
net::URLRequest* request,
net::SSLCertRequestInfo* cert_request_info) override {}
void OnSSLCertificateError(net::URLRequest* request,
const net::SSLInfo& ssl_info,
bool fatal) override {}
void OnResponseStarted(net::URLRequest* request, int net_error) override {
DCHECK(buf_.get());
DCHECK(read_loop_);
DCHECK_NE(net::ERR_IO_PENDING, net_error);
if (net_error == net::OK) {
ReadFromRequest(request);
} else {
QuitLoop();
}
}
void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
DCHECK_NE(net::ERR_IO_PENDING, bytes_read);
DCHECK(buf_.get());
DCHECK(read_loop_);
if (bytes_read > 0) {
ReadFromRequest(request);
} else {
QuitLoop();
}
}
private:
friend struct base::DefaultSingletonTraits<URLRequestDataJobFuzzerHarness>;
base::test::ScopedTaskScheduler scoped_task_scheduler_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
net::TestURLRequestContext context_;
net::URLRequestJobFactoryImpl job_factory_;
std::vector<size_t> read_lengths_;
scoped_refptr<net::IOBuffer> buf_;
base::RunLoop* read_loop_;
DISALLOW_COPY_AND_ASSIGN(URLRequestDataJobFuzzerHarness);
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Using a static singleton test harness lets the test run ~3-4x faster.
return URLRequestDataJobFuzzerHarness::GetInstance()
->CreateAndReadFromDataURLRequest(data, size);
}