Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "net/socket/fuzzed_socket.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/location.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/thread_task_runner_handle.h" | |
| 11 #include "net/base/io_buffer.h" | |
| 12 | |
| 13 namespace net { | |
| 14 | |
| 15 namespace { | |
| 16 | |
| 17 // Subset of the socket errors that can be returned by normal socket reads / | |
| 18 // writes. The first one is returned when no more seed data remains, so it's one | |
| 19 // of the most common ones. | |
| 20 const Error kReadWriteErrors[] = {ERR_CONNECTION_CLOSED, ERR_FAILED, | |
| 21 ERR_TIMED_OUT, ERR_CONNECTION_RESET}; | |
| 22 | |
| 23 } // namespace | |
| 24 | |
| 25 FuzzedSocket::FuzzedSocket(const uint8_t* data, | |
| 26 size_t data_size, | |
| 27 const BoundNetLog& bound_net_log) | |
| 28 : seed_(reinterpret_cast<const char*>(data), data_size), | |
| 29 bound_net_log_(bound_net_log), | |
| 30 weak_factory_(this) {} | |
| 31 | |
| 32 FuzzedSocket::~FuzzedSocket() {} | |
| 33 | |
| 34 int FuzzedSocket::Read(IOBuffer* buf, | |
| 35 int buf_len, | |
| 36 const CompletionCallback& callback) { | |
| 37 DCHECK(!read_pending_); | |
|
eroman
2016/04/14 17:56:27
Do we run DCHECKs in fuzzers?
Seems like we defin
mmenke
2016/04/14 19:01:51
Good question...I *think* we do, but I've switched
eroman
2016/04/14 19:32:39
(At least when I build fuzzers locally following t
| |
| 38 | |
| 39 bool sync; | |
| 40 int result; | |
| 41 | |
| 42 if (net_error_ != OK) { | |
| 43 // If an error has already been generated, use it to determine what to do. | |
| 44 result = net_error_; | |
| 45 sync = !error_pending_; | |
| 46 } else { | |
| 47 // Otherwise, use a random seed. | |
| 48 uint8_t random_val = GetUint8FromSeed(); | |
| 49 sync = !!(random_val & 0x01); | |
| 50 result = random_val >> 1; | |
| 51 if (result > buf_len) | |
| 52 result = buf_len; | |
| 53 if (static_cast<size_t>(result) > seed_.length()) | |
| 54 result = seed_.length(); | |
| 55 if (result == 0) { | |
| 56 net_error_ = GetReadWriteErrorFromSeed(); | |
| 57 result = net_error_; | |
| 58 if (!sync) | |
| 59 error_pending_ = true; | |
| 60 } else { | |
| 61 memcpy(buf->data(), seed_.data(), result); | |
| 62 seed_ = seed_.substr(result); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 // Graceful close of a socket returns OK, at least in theory. This doesn't | |
| 67 // perfectly reflect real socket behavior, but close enough. | |
| 68 if (result == ERR_CONNECTION_CLOSED) | |
| 69 result = OK; | |
|
eroman
2016/04/14 17:56:27
This is implementing the socket interface.
So thro
mmenke
2016/04/14 19:01:51
Done.
| |
| 70 | |
| 71 if (sync) { | |
| 72 if (result > 0) | |
| 73 total_bytes_read_ += result; | |
| 74 return result; | |
| 75 } | |
| 76 | |
| 77 read_pending_ = true; | |
| 78 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 79 FROM_HERE, base::Bind(&FuzzedSocket::OnReadComplete, | |
| 80 weak_factory_.GetWeakPtr(), callback, result)); | |
| 81 return ERR_IO_PENDING; | |
| 82 } | |
| 83 | |
| 84 int FuzzedSocket::Write(IOBuffer* buf, | |
| 85 int buf_len, | |
| 86 const CompletionCallback& callback) { | |
| 87 DCHECK(!write_pending_); | |
|
eroman
2016/04/14 17:56:27
Same dcheck question.
mmenke
2016/04/14 19:01:51
Done.
| |
| 88 | |
| 89 bool sync; | |
| 90 int result; | |
| 91 | |
| 92 if (net_error_ != OK) { | |
| 93 // If an error has already been generated, use it to determine what to do. | |
| 94 result = net_error_; | |
| 95 sync = !error_pending_; | |
| 96 } else { | |
| 97 // Otherwise, use a random seed. | |
| 98 uint8_t random_val = GetUint8FromSeed(); | |
| 99 sync = !!(random_val & 0x01); | |
| 100 result = random_val >> 1; | |
| 101 if (result > buf_len) | |
| 102 result = buf_len; | |
| 103 if (static_cast<size_t>(result) > seed_.length()) | |
| 104 result = seed_.length(); | |
| 105 if (static_cast<size_t>(result) > seed_.length()) | |
|
eroman
2016/04/14 17:56:27
duplicated line?
mmenke
2016/04/14 19:01:51
Done. Actually, removed *both* copies, as they're
| |
| 106 result = seed_.length(); | |
| 107 if (result == 0) { | |
| 108 net_error_ = GetReadWriteErrorFromSeed(); | |
| 109 result = net_error_; | |
| 110 if (!sync) | |
| 111 error_pending_ = true; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 if (sync) { | |
| 116 if (result > 0) | |
| 117 total_bytes_written_ += result; | |
| 118 return result; | |
| 119 } | |
| 120 | |
| 121 write_pending_ = true; | |
| 122 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 123 FROM_HERE, base::Bind(&FuzzedSocket::OnWriteComplete, | |
| 124 weak_factory_.GetWeakPtr(), callback, result)); | |
| 125 return ERR_IO_PENDING; | |
| 126 } | |
| 127 | |
| 128 int FuzzedSocket::SetReceiveBufferSize(int32_t size) { | |
| 129 return OK; | |
| 130 } | |
| 131 | |
| 132 int FuzzedSocket::SetSendBufferSize(int32_t size) { | |
| 133 return OK; | |
| 134 } | |
| 135 | |
| 136 int FuzzedSocket::Connect(const CompletionCallback& callback) { | |
| 137 // Sockets can normally be reused, but don't support it here. | |
| 138 DCHECK_NE(net_error_, OK); | |
| 139 DCHECK(!read_pending_); | |
| 140 DCHECK(!write_pending_); | |
| 141 DCHECK(!error_pending_); | |
| 142 DCHECK(!total_bytes_read_); | |
| 143 DCHECK(!total_bytes_written_); | |
| 144 | |
| 145 net_error_ = OK; | |
| 146 return OK; | |
|
eroman
2016/04/14 17:56:27
Might consider fuzzing this too (use first byte to
mmenke
2016/04/14 19:01:51
It's not relevant for these fuzzers. I'm thinking
| |
| 147 } | |
| 148 | |
| 149 void FuzzedSocket::Disconnect() { | |
| 150 net_error_ = ERR_CONNECTION_CLOSED; | |
| 151 weak_factory_.InvalidateWeakPtrs(); | |
| 152 read_pending_ = false; | |
| 153 write_pending_ = false; | |
| 154 error_pending_ = false; | |
| 155 } | |
| 156 | |
| 157 bool FuzzedSocket::IsConnected() const { | |
| 158 return net_error_ != OK && !error_pending_; | |
| 159 } | |
| 160 | |
| 161 bool FuzzedSocket::IsConnectedAndIdle() const { | |
| 162 return IsConnected(); | |
| 163 } | |
| 164 | |
| 165 int FuzzedSocket::GetPeerAddress(IPEndPoint* address) const { | |
| 166 if (!IsConnected()) | |
| 167 return ERR_SOCKET_NOT_CONNECTED; | |
| 168 *address = IPEndPoint(IPAddress(127, 0, 0, 1), 80); | |
| 169 return OK; | |
| 170 } | |
| 171 | |
| 172 int FuzzedSocket::GetLocalAddress(IPEndPoint* address) const { | |
| 173 if (!IsConnected()) | |
| 174 return ERR_SOCKET_NOT_CONNECTED; | |
| 175 *address = IPEndPoint(IPAddress(127, 0, 0, 1), 43434); | |
| 176 return OK; | |
| 177 } | |
| 178 | |
| 179 const BoundNetLog& FuzzedSocket::NetLog() const { | |
| 180 return bound_net_log_; | |
| 181 } | |
| 182 | |
| 183 void FuzzedSocket::SetSubresourceSpeculation() {} | |
| 184 | |
| 185 void FuzzedSocket::SetOmniboxSpeculation() {} | |
| 186 | |
| 187 bool FuzzedSocket::WasEverUsed() const { | |
| 188 return total_bytes_written_ != 0 || total_bytes_read_ != 0; | |
|
eroman
2016/04/14 17:56:27
technically in the case where we returned 0 from r
mmenke
2016/04/14 19:01:51
This is actually duplicating tcp_client_socket's b
| |
| 189 } | |
| 190 | |
| 191 void FuzzedSocket::EnableTCPFastOpenIfSupported() {} | |
| 192 | |
| 193 bool FuzzedSocket::WasNpnNegotiated() const { | |
| 194 return false; | |
| 195 } | |
| 196 | |
| 197 NextProto FuzzedSocket::GetNegotiatedProtocol() const { | |
| 198 return kProtoUnknown; | |
| 199 } | |
| 200 | |
| 201 bool FuzzedSocket::GetSSLInfo(SSLInfo* ssl_info) { | |
| 202 return false; | |
| 203 } | |
| 204 | |
| 205 void FuzzedSocket::GetConnectionAttempts(ConnectionAttempts* out) const { | |
| 206 out->clear(); | |
| 207 } | |
| 208 | |
| 209 void FuzzedSocket::ClearConnectionAttempts() {} | |
| 210 | |
| 211 void FuzzedSocket::AddConnectionAttempts(const ConnectionAttempts& attempts) {} | |
| 212 | |
| 213 int64_t FuzzedSocket::GetTotalReceivedBytes() const { | |
| 214 return total_bytes_read_; | |
| 215 } | |
| 216 | |
| 217 uint8_t FuzzedSocket::GetUint8FromSeed() { | |
|
eroman
2016/04/14 17:56:27
optional: Instead of "Get*" I would suggest someth
mmenke
2016/04/14 19:01:51
Done. Went with Consume*FromData.
| |
| 218 size_t length = seed_.length(); | |
| 219 if (!length) | |
| 220 return 0; | |
| 221 uint8_t out = seed_[length - 1]; | |
| 222 seed_ = seed_.substr(0, length - 1); | |
| 223 return out; | |
| 224 } | |
| 225 | |
| 226 Error FuzzedSocket::GetReadWriteErrorFromSeed() { | |
| 227 return kReadWriteErrors[GetUint8FromSeed() % arraysize(kReadWriteErrors)]; | |
| 228 } | |
| 229 | |
| 230 void FuzzedSocket::OnReadComplete(const CompletionCallback& callback, | |
| 231 int result) { | |
| 232 DCHECK(read_pending_); | |
| 233 read_pending_ = false; | |
| 234 if (result <= 0) { | |
| 235 error_pending_ = false; | |
| 236 } else { | |
| 237 total_bytes_read_ += result; | |
| 238 } | |
| 239 callback.Run(result); | |
| 240 } | |
| 241 | |
| 242 void FuzzedSocket::OnWriteComplete(const CompletionCallback& callback, | |
| 243 int result) { | |
| 244 DCHECK(write_pending_); | |
| 245 write_pending_ = false; | |
| 246 if (result <= 0) { | |
| 247 error_pending_ = false; | |
| 248 } else { | |
| 249 total_bytes_written_ += result; | |
| 250 } | |
| 251 callback.Run(result); | |
| 252 } | |
| 253 | |
| 254 } // namespace net | |
| OLD | NEW |