1 // Copyright 2016 The Chromium Authors
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 "net/socket/fuzzed_socket.h"
6
7 #include <fuzzer/FuzzedDataProvider.h>
8
9 #include "base/check_op.h"
10 #include "base/functional/bind.h"
11 #include "base/location.h"
12 #include "base/notreached.h"
13 #include "base/ranges/algorithm.h"
14 #include "base/task/single_thread_task_runner.h"
15 #include "net/base/io_buffer.h"
16 #include "net/log/net_log_source_type.h"
17 #include "net/traffic_annotation/network_traffic_annotation.h"
18
19 namespace net {
20
21 namespace {
22
23 const int kMaxAsyncReadsAndWrites = 1000;
24
25 // Some of the socket errors that can be returned by normal socket connection
26 // attempts.
27 const Error kConnectErrors[] = {
28 ERR_CONNECTION_RESET, ERR_CONNECTION_CLOSED, ERR_FAILED,
29 ERR_CONNECTION_TIMED_OUT, ERR_ACCESS_DENIED, ERR_CONNECTION_REFUSED,
30 ERR_ADDRESS_UNREACHABLE};
31
32 // Some of the socket errors that can be returned by normal socket reads /
33 // writes. The first one is returned when no more input data remains, so it's
34 // one of the most common ones.
35 const Error kReadWriteErrors[] = {ERR_CONNECTION_CLOSED, ERR_FAILED,
36 ERR_TIMED_OUT, ERR_CONNECTION_RESET};
37
38 } // namespace
39
FuzzedSocket(FuzzedDataProvider * data_provider,net::NetLog * net_log)40 FuzzedSocket::FuzzedSocket(FuzzedDataProvider* data_provider,
41 net::NetLog* net_log)
42 : data_provider_(data_provider),
43 net_log_(NetLogWithSource::Make(net_log, NetLogSourceType::SOCKET)),
44 remote_address_(IPEndPoint(IPAddress::IPv4Localhost(), 80)) {}
45
46 FuzzedSocket::~FuzzedSocket() = default;
47
Read(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)48 int FuzzedSocket::Read(IOBuffer* buf,
49 int buf_len,
50 CompletionOnceCallback callback) {
51 DCHECK(!connect_pending_);
52 DCHECK(!read_pending_);
53
54 bool sync;
55 int result;
56
57 if (net_error_ != OK) {
58 // If an error has already been generated, use it to determine what to do.
59 result = net_error_;
60 sync = !error_pending_;
61 } else {
62 // Otherwise, use |data_provider_|. Always consume a bool, even when
63 // ForceSync() is true, to behave more consistently against input mutations.
64 sync = data_provider_->ConsumeBool() || ForceSync();
65
66 num_async_reads_and_writes_ += static_cast<int>(!sync);
67
68 std::string data = data_provider_->ConsumeRandomLengthString(buf_len);
69 result = data.size();
70
71 if (!data.empty()) {
72 base::ranges::copy(data, buf->data());
73 } else {
74 result = ConsumeReadWriteErrorFromData();
75 net_error_ = result;
76 if (!sync)
77 error_pending_ = true;
78 }
79 }
80
81 // Graceful close of a socket returns OK, at least in theory. This doesn't
82 // perfectly reflect real socket behavior, but close enough.
83 if (result == ERR_CONNECTION_CLOSED)
84 result = 0;
85
86 if (sync) {
87 if (result > 0)
88 total_bytes_read_ += result;
89 return result;
90 }
91
92 read_pending_ = true;
93 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
94 FROM_HERE,
95 base::BindOnce(&FuzzedSocket::OnReadComplete, weak_factory_.GetWeakPtr(),
96 std::move(callback), result));
97 return ERR_IO_PENDING;
98 }
99
Write(IOBuffer * buf,int buf_len,CompletionOnceCallback callback,const NetworkTrafficAnnotationTag &)100 int FuzzedSocket::Write(
101 IOBuffer* buf,
102 int buf_len,
103 CompletionOnceCallback callback,
104 const NetworkTrafficAnnotationTag& /* traffic_annotation */) {
105 DCHECK(!connect_pending_);
106 DCHECK(!write_pending_);
107
108 bool sync;
109 int result;
110
111 if (net_error_ != OK) {
112 // If an error has already been generated, use it to determine what to do.
113 result = net_error_;
114 sync = !error_pending_;
115 } else {
116 // Otherwise, use |data_provider_|. Always consume a bool, even when
117 // ForceSync() is true, to behave more consistently against input mutations.
118 sync = data_provider_->ConsumeBool() || ForceSync();
119
120 num_async_reads_and_writes_ += static_cast<int>(!sync);
121
122 // Intentionally using smaller |result| size here.
123 result = data_provider_->ConsumeIntegralInRange<int>(0, 0xFF);
124 if (result > buf_len)
125 result = buf_len;
126 if (result == 0) {
127 net_error_ = ConsumeReadWriteErrorFromData();
128 result = net_error_;
129 if (!sync)
130 error_pending_ = true;
131 }
132 }
133
134 if (sync) {
135 if (result > 0)
136 total_bytes_written_ += result;
137 return result;
138 }
139
140 write_pending_ = true;
141 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
142 FROM_HERE,
143 base::BindOnce(&FuzzedSocket::OnWriteComplete, weak_factory_.GetWeakPtr(),
144 std::move(callback), result));
145 return ERR_IO_PENDING;
146 }
147
SetReceiveBufferSize(int32_t size)148 int FuzzedSocket::SetReceiveBufferSize(int32_t size) {
149 return OK;
150 }
151
SetSendBufferSize(int32_t size)152 int FuzzedSocket::SetSendBufferSize(int32_t size) {
153 return OK;
154 }
155
Bind(const net::IPEndPoint & local_addr)156 int FuzzedSocket::Bind(const net::IPEndPoint& local_addr) {
157 NOTREACHED();
158 }
159
Connect(CompletionOnceCallback callback)160 int FuzzedSocket::Connect(CompletionOnceCallback callback) {
161 // Sockets can normally be reused, but don't support it here.
162 DCHECK_NE(net_error_, OK);
163 DCHECK(!connect_pending_);
164 DCHECK(!read_pending_);
165 DCHECK(!write_pending_);
166 DCHECK(!error_pending_);
167 DCHECK(!total_bytes_read_);
168 DCHECK(!total_bytes_written_);
169
170 bool sync = true;
171 Error result = OK;
172 if (fuzz_connect_result_) {
173 // Decide if sync or async. Use async, if no data is left.
174 sync = data_provider_->ConsumeBool();
175 // Decide if the connect succeeds or not, and if so, pick an error code.
176 if (data_provider_->ConsumeBool())
177 result = data_provider_->PickValueInArray(kConnectErrors);
178 }
179
180 if (sync) {
181 net_error_ = result;
182 return result;
183 }
184
185 connect_pending_ = true;
186 if (result != OK)
187 error_pending_ = true;
188 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
189 FROM_HERE,
190 base::BindOnce(&FuzzedSocket::OnConnectComplete,
191 weak_factory_.GetWeakPtr(), std::move(callback), result));
192 return ERR_IO_PENDING;
193 }
194
Disconnect()195 void FuzzedSocket::Disconnect() {
196 net_error_ = ERR_CONNECTION_CLOSED;
197 weak_factory_.InvalidateWeakPtrs();
198 connect_pending_ = false;
199 read_pending_ = false;
200 write_pending_ = false;
201 error_pending_ = false;
202 }
203
IsConnected() const204 bool FuzzedSocket::IsConnected() const {
205 return net_error_ == OK && !error_pending_;
206 }
207
IsConnectedAndIdle() const208 bool FuzzedSocket::IsConnectedAndIdle() const {
209 return IsConnected();
210 }
211
GetPeerAddress(IPEndPoint * address) const212 int FuzzedSocket::GetPeerAddress(IPEndPoint* address) const {
213 if (!IsConnected())
214 return ERR_SOCKET_NOT_CONNECTED;
215 *address = remote_address_;
216 return OK;
217 }
218
GetLocalAddress(IPEndPoint * address) const219 int FuzzedSocket::GetLocalAddress(IPEndPoint* address) const {
220 if (!IsConnected())
221 return ERR_SOCKET_NOT_CONNECTED;
222 *address = IPEndPoint(IPAddress(127, 0, 0, 1), 43434);
223 return OK;
224 }
225
NetLog() const226 const NetLogWithSource& FuzzedSocket::NetLog() const {
227 return net_log_;
228 }
229
WasEverUsed() const230 bool FuzzedSocket::WasEverUsed() const {
231 return total_bytes_written_ != 0 || total_bytes_read_ != 0;
232 }
233
GetNegotiatedProtocol() const234 NextProto FuzzedSocket::GetNegotiatedProtocol() const {
235 return kProtoUnknown;
236 }
237
GetSSLInfo(SSLInfo * ssl_info)238 bool FuzzedSocket::GetSSLInfo(SSLInfo* ssl_info) {
239 return false;
240 }
241
GetTotalReceivedBytes() const242 int64_t FuzzedSocket::GetTotalReceivedBytes() const {
243 return total_bytes_read_;
244 }
245
ApplySocketTag(const net::SocketTag & tag)246 void FuzzedSocket::ApplySocketTag(const net::SocketTag& tag) {}
247
ConsumeReadWriteErrorFromData()248 Error FuzzedSocket::ConsumeReadWriteErrorFromData() {
249 return data_provider_->PickValueInArray(kReadWriteErrors);
250 }
251
OnReadComplete(CompletionOnceCallback callback,int result)252 void FuzzedSocket::OnReadComplete(CompletionOnceCallback callback, int result) {
253 CHECK(read_pending_);
254 read_pending_ = false;
255 if (result <= 0) {
256 error_pending_ = false;
257 } else {
258 total_bytes_read_ += result;
259 }
260 std::move(callback).Run(result);
261 }
262
OnWriteComplete(CompletionOnceCallback callback,int result)263 void FuzzedSocket::OnWriteComplete(CompletionOnceCallback callback,
264 int result) {
265 CHECK(write_pending_);
266 write_pending_ = false;
267 if (result <= 0) {
268 error_pending_ = false;
269 } else {
270 total_bytes_written_ += result;
271 }
272 std::move(callback).Run(result);
273 }
274
OnConnectComplete(CompletionOnceCallback callback,int result)275 void FuzzedSocket::OnConnectComplete(CompletionOnceCallback callback,
276 int result) {
277 CHECK(connect_pending_);
278 connect_pending_ = false;
279 if (result < 0)
280 error_pending_ = false;
281 net_error_ = result;
282 std::move(callback).Run(result);
283 }
284
ForceSync() const285 bool FuzzedSocket::ForceSync() const {
286 return (num_async_reads_and_writes_ >= kMaxAsyncReadsAndWrites);
287 }
288
289 } // namespace net
290