blob: 61979c7479929f70d20827620de9e26759bb4be0 [file] [log] [blame]
// Copyright 2021 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 "src/sys/fuzzing/common/async-socket.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/socket.h>
#include <zircon/status.h>
namespace fuzzing {
namespace {
struct TransferParams {
ExecutorPtr executor;
const char* label;
zx::socket socket;
Input input;
zx_signals_t signals;
};
template <typename Transfer>
ZxPromise<Input> AsyncSocketTransfer(TransferParams&& params, Transfer transfer) {
FX_DCHECK(params.executor);
return fpromise::make_promise([executor = params.executor, label = params.label,
socket = std::move(params.socket), input = std::move(params.input),
signals = params.signals, transfer = std::move(transfer),
offset = size_t(0), awaiting = ZxFuture<zx_signals_t>()](
Context& context) mutable -> ZxResult<Input> {
while (true) {
if (offset == input.size()) {
return fpromise::ok(std::move(input));
}
size_t actual = 0;
auto status = transfer(socket, input.data() + offset, input.size() - offset, &actual);
if (status == ZX_OK) {
offset += actual;
FX_DCHECK(offset <= input.size());
} else if (status != ZX_ERR_SHOULD_WAIT) {
FX_LOGS(WARNING) << "Failed to " << label << " socket: " << zx_status_get_string(status);
return fpromise::error(status);
}
if (offset == input.size()) {
return fpromise::ok(std::move(input));
}
if (!awaiting) {
awaiting =
executor
->MakePromiseWaitHandle(zx::unowned_handle(socket.get()),
signals | ZX_SOCKET_PEER_CLOSED)
.and_then([signals](const zx_packet_signal_t& packet) -> ZxResult<zx_signals_t> {
if (packet.observed & ZX_SOCKET_PEER_CLOSED) {
return fpromise::error(ZX_ERR_PEER_CLOSED);
}
return fpromise::ok(packet.observed & signals);
});
}
if (!awaiting(context)) {
return fpromise::pending();
}
if (awaiting.is_error()) {
auto status = awaiting.error();
FX_LOGS(WARNING) << "Failed to " << label << " socket: " << zx_status_get_string(status);
return fpromise::error(status);
}
awaiting = nullptr;
}
});
}
} // namespace
ZxPromise<Input> AsyncSocketRead(const ExecutorPtr& executor, FidlInput&& fidl_input) {
TransferParams params = {
.executor = executor,
.label = "read from",
.socket = std::move(fidl_input.socket),
.input = Input(fidl_input.size),
.signals = ZX_SOCKET_READABLE | ZX_SOCKET_PEER_WRITE_DISABLED,
};
return AsyncSocketTransfer(
std::move(params), [](const zx::socket& socket, uint8_t* buf, size_t len, size_t* actual) {
return socket.read(0, buf, len, actual);
});
}
ZxPromise<Artifact> AsyncSocketRead(const ExecutorPtr& executor, FidlArtifact&& fidl_artifact) {
FuzzResult fuzz_result;
FidlInput fidl_input;
std::tie(fuzz_result, fidl_input) = std::move(fidl_artifact);
return AsyncSocketRead(executor, std::move(fidl_input)).and_then([fuzz_result](Input& input) {
return fpromise::ok(Artifact(fuzz_result, std::move(input)));
});
}
FidlInput AsyncSocketWrite(const ExecutorPtr& executor, Input&& input) {
FidlInput fidl_input;
fidl_input.size = input.size();
zx::socket socket;
auto status = zx::socket::create(ZX_SOCKET_STREAM, &socket, &fidl_input.socket);
FX_DCHECK(status == ZX_OK) << zx_status_get_string(status);
fidl_input.socket.set_disposition(ZX_SOCKET_DISPOSITION_WRITE_DISABLED, 0);
FX_DCHECK(status == ZX_OK) << zx_status_get_string(status);
TransferParams params = {
.executor = executor,
.label = "write to",
.socket = std::move(socket),
.input = std::move(input),
.signals = ZX_SOCKET_WRITABLE,
};
auto task = AsyncSocketTransfer(std::move(params), [](const zx::socket& socket, uint8_t* buf,
size_t len, size_t* actual) {
return socket.write(0, buf, len, actual);
}).and_then([](Input& input) -> ZxResult<> { return fpromise::ok(); });
executor->schedule_task(std::move(task));
return fidl_input;
}
FidlArtifact AsyncSocketWrite(const ExecutorPtr& executor, Artifact&& artifact) {
auto fidl_input = AsyncSocketWrite(executor, artifact.take_input());
return MakeFidlArtifact(artifact.fuzz_result(), std::move(fidl_input));
}
} // namespace fuzzing