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 return ERR_NOT_IMPLEMENTED;
159 }
160
Connect(CompletionOnceCallback callback)161 int FuzzedSocket::Connect(CompletionOnceCallback callback) {
162 // Sockets can normally be reused, but don't support it here.
163 DCHECK_NE(net_error_, OK);
164 DCHECK(!connect_pending_);
165 DCHECK(!read_pending_);
166 DCHECK(!write_pending_);
167 DCHECK(!error_pending_);
168 DCHECK(!total_bytes_read_);
169 DCHECK(!total_bytes_written_);
170
171 bool sync = true;
172 Error result = OK;
173 if (fuzz_connect_result_) {
174 // Decide if sync or async. Use async, if no data is left.
175 sync = data_provider_->ConsumeBool();
176 // Decide if the connect succeeds or not, and if so, pick an error code.
177 if (data_provider_->ConsumeBool())
178 result = data_provider_->PickValueInArray(kConnectErrors);
179 }
180
181 if (sync) {
182 net_error_ = result;
183 return result;
184 }
185
186 connect_pending_ = true;
187 if (result != OK)
188 error_pending_ = true;
189 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
190 FROM_HERE,
191 base::BindOnce(&FuzzedSocket::OnConnectComplete,
192 weak_factory_.GetWeakPtr(), std::move(callback), result));
193 return ERR_IO_PENDING;
194 }
195
Disconnect()196 void FuzzedSocket::Disconnect() {
197 net_error_ = ERR_CONNECTION_CLOSED;
198 weak_factory_.InvalidateWeakPtrs();
199 connect_pending_ = false;
200 read_pending_ = false;
201 write_pending_ = false;
202 error_pending_ = false;
203 }
204
IsConnected() const205 bool FuzzedSocket::IsConnected() const {
206 return net_error_ == OK && !error_pending_;
207 }
208
IsConnectedAndIdle() const209 bool FuzzedSocket::IsConnectedAndIdle() const {
210 return IsConnected();
211 }
212
GetPeerAddress(IPEndPoint * address) const213 int FuzzedSocket::GetPeerAddress(IPEndPoint* address) const {
214 if (!IsConnected())
215 return ERR_SOCKET_NOT_CONNECTED;
216 *address = remote_address_;
217 return OK;
218 }
219
GetLocalAddress(IPEndPoint * address) const220 int FuzzedSocket::GetLocalAddress(IPEndPoint* address) const {
221 if (!IsConnected())
222 return ERR_SOCKET_NOT_CONNECTED;
223 *address = IPEndPoint(IPAddress(127, 0, 0, 1), 43434);
224 return OK;
225 }
226
NetLog() const227 const NetLogWithSource& FuzzedSocket::NetLog() const {
228 return net_log_;
229 }
230
WasEverUsed() const231 bool FuzzedSocket::WasEverUsed() const {
232 return total_bytes_written_ != 0 || total_bytes_read_ != 0;
233 }
234
GetNegotiatedProtocol() const235 NextProto FuzzedSocket::GetNegotiatedProtocol() const {
236 return kProtoUnknown;
237 }
238
GetSSLInfo(SSLInfo * ssl_info)239 bool FuzzedSocket::GetSSLInfo(SSLInfo* ssl_info) {
240 return false;
241 }
242
GetTotalReceivedBytes() const243 int64_t FuzzedSocket::GetTotalReceivedBytes() const {
244 return total_bytes_read_;
245 }
246
ApplySocketTag(const net::SocketTag & tag)247 void FuzzedSocket::ApplySocketTag(const net::SocketTag& tag) {}
248
ConsumeReadWriteErrorFromData()249 Error FuzzedSocket::ConsumeReadWriteErrorFromData() {
250 return data_provider_->PickValueInArray(kReadWriteErrors);
251 }
252
OnReadComplete(CompletionOnceCallback callback,int result)253 void FuzzedSocket::OnReadComplete(CompletionOnceCallback callback, int result) {
254 CHECK(read_pending_);
255 read_pending_ = false;
256 if (result <= 0) {
257 error_pending_ = false;
258 } else {
259 total_bytes_read_ += result;
260 }
261 std::move(callback).Run(result);
262 }
263
OnWriteComplete(CompletionOnceCallback callback,int result)264 void FuzzedSocket::OnWriteComplete(CompletionOnceCallback callback,
265 int result) {
266 CHECK(write_pending_);
267 write_pending_ = false;
268 if (result <= 0) {
269 error_pending_ = false;
270 } else {
271 total_bytes_written_ += result;
272 }
273 std::move(callback).Run(result);
274 }
275
OnConnectComplete(CompletionOnceCallback callback,int result)276 void FuzzedSocket::OnConnectComplete(CompletionOnceCallback callback,
277 int result) {
278 CHECK(connect_pending_);
279 connect_pending_ = false;
280 if (result < 0)
281 error_pending_ = false;
282 net_error_ = result;
283 std::move(callback).Run(result);
284 }
285
ForceSync() const286 bool FuzzedSocket::ForceSync() const {
287 return (num_async_reads_and_writes_ >= kMaxAsyncReadsAndWrites);
288 }
289
290 } // namespace net
291