blob: db77b7d2fb642f5d88fc6c0d9ff4c25d093cfac9 [file] [log] [blame]
// Copyright 2018 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 "garnet/lib/overnet/datagram_stream/linearizer_fuzzer.h"
namespace overnet {
namespace linearizer_fuzzer {
// Close the linearizer with some status code.
void LinearizerFuzzer::Close(uint8_t status_code) {
if (status_code == 0) {
if (!length_.has_value()) {
status_code = 1;
} else if (offset_ != *length_) {
status_code = 1;
} else {
for (uint64_t i = 0; i < *length_; i++) {
if (!bytes_[i].present) {
status_code = 1;
}
}
}
}
SetClosed(static_cast<StatusCode>(status_code));
linearizer_.Close(static_cast<StatusCode>(status_code)).Ignore();
}
// Push a new block onto the linearizer at offset 'offset', with length
// 'length', an end_of_message flag, and data bytes in 'data'.
void LinearizerFuzzer::Push(uint16_t offset, uint8_t length,
bool end_of_message, const uint8_t* data) {
uint64_t last_byte = static_cast<uint64_t>(offset) + length;
const bool resource_exhausted = last_byte > offset_ + kBuffer;
if (!resource_exhausted) {
if (length_) {
if (last_byte > *length_) {
SetClosed(StatusCode::INVALID_ARGUMENT);
} else if (end_of_message && *length_ != last_byte) {
SetClosed(StatusCode::INVALID_ARGUMENT);
}
} else if (end_of_message) {
if (offset_ > last_byte)
SetClosed(StatusCode::INVALID_ARGUMENT);
for (unsigned i = last_byte; i < sizeof(bytes_) / sizeof(*bytes_); i++) {
if (bytes_[i].present)
SetClosed(StatusCode::INVALID_ARGUMENT);
}
if (offset_ == last_byte)
SetClosed(StatusCode::OK);
length_ = last_byte;
}
for (unsigned i = 0; i < length; i++) {
unsigned idx = i + offset;
ByteState& st = bytes_[idx];
if (st.present) {
if (st.byte != data[i] && idx >= offset_) {
SetClosed(StatusCode::DATA_LOSS);
}
} else {
st.byte = data[i];
st.present = true;
}
}
}
auto ignore = [](bool) {};
ignore(linearizer_.Push(
Chunk{offset, end_of_message, Slice::FromCopiedBuffer(data, length)}));
}
// Execute a pull op on the linearizer, and verify that it's as expected.
void LinearizerFuzzer::Pull() {
if (waiting_for_pull_)
return;
waiting_for_pull_ = true;
linearizer_.Pull(StatusOrCallback<Optional<Slice>>(
[this](const StatusOr<Optional<Slice>>& status) {
assert(waiting_for_pull_);
waiting_for_pull_ = false;
#ifndef NDEBUG
if (is_closed_) {
assert(status.code() == closed_status_);
} else {
assert(status.is_ok());
if (*status) {
for (auto b : **status) {
ByteState st = bytes_[offset_++];
assert(st.present);
assert(b == st.byte);
}
}
if (length_ && *length_ == offset_) {
SetClosed(StatusCode::OK);
}
}
#endif
}));
}
} // namespace linearizer_fuzzer
} // namespace overnet