blob: 8108b9aec1cfc01c0832b78cdc765c71aefac31f [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <iostream>
#include "src/connectivity/overnet/lib/datagram_stream/stream_state.h"
using namespace overnet;
namespace {
bool logging = false;
#define FUZZLOG \
if (!logging) \
; \
else \
std::cout
class InputStream {
public:
InputStream(const uint8_t* data, size_t size)
: cur_(data), end_(data + size) {}
uint8_t NextByte() {
if (cur_ == end_)
return 0;
return *cur_++;
}
bool NextBool() { return (NextByte() & 1) != 0; }
std::string NextString() {
int length = NextByte();
std::string out;
for (int i = 0; i < length; i++) {
out.push_back(NextByte());
}
return out;
}
Status NextStatus() {
auto code = static_cast<StatusCode>(NextByte());
if (NextBool()) {
auto reason = NextString();
return Status(code, reason);
} else {
return Status(code);
}
}
private:
const uint8_t* cur_;
const uint8_t* end_;
};
class Fuzzer : private StreamStateListener {
public:
StreamState state{this};
bool stopped_reading = false;
bool stream_closed = false;
int quiesce_functions_made = 0;
int quiesce_functions_called = 0;
auto MakeQuiesceFunction() {
quiesce_functions_made++;
return [this] { quiesce_functions_called++; };
}
bool TakeAck() {
if (can_ack) {
can_ack = false;
return true;
}
return false;
}
void CheckDescription() {
if (logging) {
std::cout << "state=" << state.Description() << '\n';
} else {
state.Description();
}
}
void ValidateState() {
CheckDescription();
if (state.CanBeginSend()) {
assert(state.IsOpenForSending());
}
if (state.IsOpenForSending()) {
assert(state.CanBeginOp());
}
if (state.IsOpenForReceiving()) {
assert(state.CanBeginOp());
}
if (state.IsOpenForSending() || state.IsOpenForReceiving()) {
assert(!stream_closed);
}
if (state.IsClosedForReceiving()) {
assert(stopped_reading);
} else {
assert(!stopped_reading);
}
}
private:
bool can_ack = false;
int close_retries;
Optional<Status> last_sent_close;
void SendClose() {
FUZZLOG << "--> SendClose()\n";
CheckDescription();
assert(!can_ack);
auto status = state.GetSendStatus();
can_ack = true;
if (status.code() != last_sent_close.Map([](const Status& status) {
return status.code();
})) {
close_retries = 0;
last_sent_close = status;
} else {
close_retries++;
assert(close_retries <= StreamState::kMaxCloseRetries);
}
}
void StopReading(const Status& status) {
FUZZLOG << "--> StopReading(" << status << ")\n";
CheckDescription();
assert(!stopped_reading);
stopped_reading = true;
assert(state.IsClosedForReceiving());
}
void StreamClosed() {
FUZZLOG << "--> StreamClosed()\n";
CheckDescription();
assert(stopped_reading);
assert(!stream_closed);
stream_closed = true;
assert(state.IsClosedForReceiving());
assert(state.IsClosedForSending());
}
};
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
InputStream input(data, size);
Fuzzer fuzzer;
int ops_begun = 0;
int sends_begun = 0;
bool quiesce_ready_called = false;
while (fuzzer.quiesce_functions_called < 1) {
FUZZLOG << "------\n";
fuzzer.ValidateState();
switch (input.NextByte()) {
case 1: {
auto status = input.NextStatus();
FUZZLOG << "LocalClose(" << status << ")\n";
fuzzer.state.LocalClose(status, fuzzer.MakeQuiesceFunction());
} break;
case 2: {
auto status = input.NextStatus();
FUZZLOG << "RemoteClose(" << status << ")\n";
fuzzer.state.RemoteClose(status);
} break;
case 3: {
if (fuzzer.TakeAck()) {
auto status = input.NextStatus();
FUZZLOG << "SendCloseAck(" << status << ")\n";
fuzzer.state.SendCloseAck(status);
}
} break;
case 4: {
if (fuzzer.state.CanBeginOp()) {
FUZZLOG << "BeginOp()\n";
ops_begun++;
fuzzer.state.BeginOp();
}
} break;
case 5:
if (ops_begun) {
FUZZLOG << "EndOp()\n";
ops_begun--;
fuzzer.state.EndOp();
}
break;
case 6:
if (fuzzer.stream_closed && !quiesce_ready_called) {
FUZZLOG << "QuiesceReady()\n";
quiesce_ready_called = true;
fuzzer.state.QuiesceReady();
}
break;
case 7: {
if (fuzzer.state.CanBeginSend()) {
FUZZLOG << "BeginSend()\n";
sends_begun++;
fuzzer.state.BeginSend();
}
} break;
case 8:
if (sends_begun) {
FUZZLOG << "EndSend()\n";
sends_begun--;
fuzzer.state.EndSend();
}
break;
default:
FUZZLOG << "Cleanup()\n";
FUZZLOG << " LocalClose(Ok)\n";
fuzzer.state.LocalClose(Status::Ok(), fuzzer.MakeQuiesceFunction());
FUZZLOG << " state --> " << fuzzer.state.Description() << "\n";
FUZZLOG << " RemoteClose(Cancelled)\n";
fuzzer.state.RemoteClose(Status::Cancelled());
FUZZLOG << " state --> " << fuzzer.state.Description() << "\n";
for (int i = 0; i < sends_begun; i++) {
FUZZLOG << " EndSend()\n";
fuzzer.state.EndSend();
FUZZLOG << " state --> " << fuzzer.state.Description() << "\n";
}
for (int i = 0; i < ops_begun; i++) {
FUZZLOG << " EndOp()\n";
fuzzer.state.EndOp();
FUZZLOG << " state --> " << fuzzer.state.Description() << "\n";
}
ops_begun = 0;
if (fuzzer.TakeAck()) {
FUZZLOG << " SendCloseAck(Cancelled)\n";
fuzzer.state.SendCloseAck(Status::Cancelled());
FUZZLOG << " state --> " << fuzzer.state.Description() << "\n";
}
assert(fuzzer.stream_closed);
if (!quiesce_ready_called) {
FUZZLOG << " QuiesceReady()\n";
quiesce_ready_called = true;
fuzzer.state.QuiesceReady();
FUZZLOG << " state --> " << fuzzer.state.Description() << "\n";
}
break;
}
}
FUZZLOG << "------\n";
FUZZLOG << "Done()\n";
fuzzer.ValidateState();
assert(quiesce_ready_called);
assert(ops_begun == 0);
assert(fuzzer.quiesce_functions_called == fuzzer.quiesce_functions_made);
return 0;
}