Chromium Code Reviews| Index: net/socket/fuzzed_socket.cc |
| diff --git a/net/socket/fuzzed_socket.cc b/net/socket/fuzzed_socket.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d9527de931ecd4be0983b0e5fe8a8d86ef5b6f84 |
| --- /dev/null |
| +++ b/net/socket/fuzzed_socket.cc |
| @@ -0,0 +1,254 @@ |
| +// 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 "net/socket/fuzzed_socket.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/location.h" |
| +#include "base/logging.h" |
| +#include "base/thread_task_runner_handle.h" |
| +#include "net/base/io_buffer.h" |
| + |
| +namespace net { |
| + |
| +namespace { |
| + |
| +// Subset of the socket errors that can be returned by normal socket reads / |
| +// writes. The first one is returned when no more seed data remains, so it's one |
| +// of the most common ones. |
| +const Error kReadWriteErrors[] = {ERR_CONNECTION_CLOSED, ERR_FAILED, |
| + ERR_TIMED_OUT, ERR_CONNECTION_RESET}; |
| + |
| +} // namespace |
| + |
| +FuzzedSocket::FuzzedSocket(const uint8_t* data, |
| + size_t data_size, |
| + const BoundNetLog& bound_net_log) |
| + : seed_(reinterpret_cast<const char*>(data), data_size), |
| + bound_net_log_(bound_net_log), |
| + weak_factory_(this) {} |
| + |
| +FuzzedSocket::~FuzzedSocket() {} |
| + |
| +int FuzzedSocket::Read(IOBuffer* buf, |
| + int buf_len, |
| + const CompletionCallback& callback) { |
| + 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
|
| + |
| + bool sync; |
| + int result; |
| + |
| + if (net_error_ != OK) { |
| + // If an error has already been generated, use it to determine what to do. |
| + result = net_error_; |
| + sync = !error_pending_; |
| + } else { |
| + // Otherwise, use a random seed. |
| + uint8_t random_val = GetUint8FromSeed(); |
| + sync = !!(random_val & 0x01); |
| + result = random_val >> 1; |
| + if (result > buf_len) |
| + result = buf_len; |
| + if (static_cast<size_t>(result) > seed_.length()) |
| + result = seed_.length(); |
| + if (result == 0) { |
| + net_error_ = GetReadWriteErrorFromSeed(); |
| + result = net_error_; |
| + if (!sync) |
| + error_pending_ = true; |
| + } else { |
| + memcpy(buf->data(), seed_.data(), result); |
| + seed_ = seed_.substr(result); |
| + } |
| + } |
| + |
| + // Graceful close of a socket returns OK, at least in theory. This doesn't |
| + // perfectly reflect real socket behavior, but close enough. |
| + if (result == ERR_CONNECTION_CLOSED) |
| + 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.
|
| + |
| + if (sync) { |
| + if (result > 0) |
| + total_bytes_read_ += result; |
| + return result; |
| + } |
| + |
| + read_pending_ = true; |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&FuzzedSocket::OnReadComplete, |
| + weak_factory_.GetWeakPtr(), callback, result)); |
| + return ERR_IO_PENDING; |
| +} |
| + |
| +int FuzzedSocket::Write(IOBuffer* buf, |
| + int buf_len, |
| + const CompletionCallback& callback) { |
| + DCHECK(!write_pending_); |
|
eroman
2016/04/14 17:56:27
Same dcheck question.
mmenke
2016/04/14 19:01:51
Done.
|
| + |
| + bool sync; |
| + int result; |
| + |
| + if (net_error_ != OK) { |
| + // If an error has already been generated, use it to determine what to do. |
| + result = net_error_; |
| + sync = !error_pending_; |
| + } else { |
| + // Otherwise, use a random seed. |
| + uint8_t random_val = GetUint8FromSeed(); |
| + sync = !!(random_val & 0x01); |
| + result = random_val >> 1; |
| + if (result > buf_len) |
| + result = buf_len; |
| + if (static_cast<size_t>(result) > seed_.length()) |
| + result = seed_.length(); |
| + 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
|
| + result = seed_.length(); |
| + if (result == 0) { |
| + net_error_ = GetReadWriteErrorFromSeed(); |
| + result = net_error_; |
| + if (!sync) |
| + error_pending_ = true; |
| + } |
| + } |
| + |
| + if (sync) { |
| + if (result > 0) |
| + total_bytes_written_ += result; |
| + return result; |
| + } |
| + |
| + write_pending_ = true; |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&FuzzedSocket::OnWriteComplete, |
| + weak_factory_.GetWeakPtr(), callback, result)); |
| + return ERR_IO_PENDING; |
| +} |
| + |
| +int FuzzedSocket::SetReceiveBufferSize(int32_t size) { |
| + return OK; |
| +} |
| + |
| +int FuzzedSocket::SetSendBufferSize(int32_t size) { |
| + return OK; |
| +} |
| + |
| +int FuzzedSocket::Connect(const CompletionCallback& callback) { |
| + // Sockets can normally be reused, but don't support it here. |
| + DCHECK_NE(net_error_, OK); |
| + DCHECK(!read_pending_); |
| + DCHECK(!write_pending_); |
| + DCHECK(!error_pending_); |
| + DCHECK(!total_bytes_read_); |
| + DCHECK(!total_bytes_written_); |
| + |
| + net_error_ = OK; |
| + 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
|
| +} |
| + |
| +void FuzzedSocket::Disconnect() { |
| + net_error_ = ERR_CONNECTION_CLOSED; |
| + weak_factory_.InvalidateWeakPtrs(); |
| + read_pending_ = false; |
| + write_pending_ = false; |
| + error_pending_ = false; |
| +} |
| + |
| +bool FuzzedSocket::IsConnected() const { |
| + return net_error_ != OK && !error_pending_; |
| +} |
| + |
| +bool FuzzedSocket::IsConnectedAndIdle() const { |
| + return IsConnected(); |
| +} |
| + |
| +int FuzzedSocket::GetPeerAddress(IPEndPoint* address) const { |
| + if (!IsConnected()) |
| + return ERR_SOCKET_NOT_CONNECTED; |
| + *address = IPEndPoint(IPAddress(127, 0, 0, 1), 80); |
| + return OK; |
| +} |
| + |
| +int FuzzedSocket::GetLocalAddress(IPEndPoint* address) const { |
| + if (!IsConnected()) |
| + return ERR_SOCKET_NOT_CONNECTED; |
| + *address = IPEndPoint(IPAddress(127, 0, 0, 1), 43434); |
| + return OK; |
| +} |
| + |
| +const BoundNetLog& FuzzedSocket::NetLog() const { |
| + return bound_net_log_; |
| +} |
| + |
| +void FuzzedSocket::SetSubresourceSpeculation() {} |
| + |
| +void FuzzedSocket::SetOmniboxSpeculation() {} |
| + |
| +bool FuzzedSocket::WasEverUsed() const { |
| + 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
|
| +} |
| + |
| +void FuzzedSocket::EnableTCPFastOpenIfSupported() {} |
| + |
| +bool FuzzedSocket::WasNpnNegotiated() const { |
| + return false; |
| +} |
| + |
| +NextProto FuzzedSocket::GetNegotiatedProtocol() const { |
| + return kProtoUnknown; |
| +} |
| + |
| +bool FuzzedSocket::GetSSLInfo(SSLInfo* ssl_info) { |
| + return false; |
| +} |
| + |
| +void FuzzedSocket::GetConnectionAttempts(ConnectionAttempts* out) const { |
| + out->clear(); |
| +} |
| + |
| +void FuzzedSocket::ClearConnectionAttempts() {} |
| + |
| +void FuzzedSocket::AddConnectionAttempts(const ConnectionAttempts& attempts) {} |
| + |
| +int64_t FuzzedSocket::GetTotalReceivedBytes() const { |
| + return total_bytes_read_; |
| +} |
| + |
| +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.
|
| + size_t length = seed_.length(); |
| + if (!length) |
| + return 0; |
| + uint8_t out = seed_[length - 1]; |
| + seed_ = seed_.substr(0, length - 1); |
| + return out; |
| +} |
| + |
| +Error FuzzedSocket::GetReadWriteErrorFromSeed() { |
| + return kReadWriteErrors[GetUint8FromSeed() % arraysize(kReadWriteErrors)]; |
| +} |
| + |
| +void FuzzedSocket::OnReadComplete(const CompletionCallback& callback, |
| + int result) { |
| + DCHECK(read_pending_); |
| + read_pending_ = false; |
| + if (result <= 0) { |
| + error_pending_ = false; |
| + } else { |
| + total_bytes_read_ += result; |
| + } |
| + callback.Run(result); |
| +} |
| + |
| +void FuzzedSocket::OnWriteComplete(const CompletionCallback& callback, |
| + int result) { |
| + DCHECK(write_pending_); |
| + write_pending_ = false; |
| + if (result <= 0) { |
| + error_pending_ = false; |
| + } else { |
| + total_bytes_written_ += result; |
| + } |
| + callback.Run(result); |
| +} |
| + |
| +} // namespace net |