• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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