blob: 5d4db649117122c0fbad695b8bce42f0ad62c5ef [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.
#ifndef LIB_FIDL_CPP_FUZZING_SERVER_PROVIDER_H_
#define LIB_FIDL_CPP_FUZZING_SERVER_PROVIDER_H_
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/task.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <lib/zx/time.h>
#include <stdio.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
namespace fidl {
namespace fuzzing {
// Enumerate the different ways`ServerProvider` objects can dispatch work to server requests.
enum ServerProviderDispatcherMode {
// Use the `async_dispatcher_t*` passed to `ServerProvider.Connect()` to dispatch work to server
// requests. This usually means that such work will be queued on the same `async::Loop` as the
// client-side work to make requests to the server.
kFromCaller = 0,
// Construct an `async::Loop` (with its associated `async_dispatcher_t*`) on a separate thread.
// The `async::Loop` will be is owned by the `ServerProvider`. This means that nothing but the
// server's work will be dispatched on said thread/loop/dispatcher. This mode is generally
// discouraged because the use of a separate thread makes it more difficult to reproduce bugs
// discovered by fuzzers. Only use this mode if dispatching server work on the same
// thread/loop/dispatcher as the client can cause a deadlock.
kOwned,
};
// Server provider that implements the server lifecycle for a generated FIDL server fuzzer
template <typename Interface, typename Impl>
class ServerProvider {
public:
explicit ServerProvider(ServerProviderDispatcherMode dispatcher_mode) {
if (dispatcher_mode == kOwned) {
loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread);
}
}
// Implementation of fuzzer-to-server library's `fuzzer_init`.
// Forward arguments to server constructor.
template <typename... Args>
zx_status_t Init(Args&&... args) {
if (impl_.get() == nullptr) {
printf("ServerProvider.Init: Creating new server instance\n");
impl_ = std::make_unique<Impl>(std::forward<Args>(args)...);
}
printf("ServerProvider.Init: return ZX_OK\n");
return ZX_OK;
}
// Implementation of fuzzer-to-server library's `fuzzer_connect`.
zx_status_t Connect(zx_handle_t channel_handle, async_dispatcher_t* dispatcher) {
if (loop_.get()) {
dispatcher = loop_->dispatcher();
}
auto binding = std::make_unique<::fidl::Binding<Interface, Impl*>>(
impl_.get(), zx::channel(channel_handle), dispatcher);
bindings_.push_back(std::move(binding));
printf("ServerProvider.Connect: return ZX_OK\n");
return ZX_OK;
}
// Implementation of fuzzer-to-server library's `fuzzer_disconnect`.
zx_status_t Disconnect(zx_handle_t channel_handle, async_dispatcher_t* dispatcher) {
for (auto it = bindings_.begin(); it != bindings_.end(); it++) {
if ((*it)->channel().get() == channel_handle) {
printf("ServerProvider.Disconnect: Closing and removing binding\n");
zx_status_t status = CloseAndDeleteBinding(it);
if (status != ZX_OK) {
printf("ServerProvider.Disconnect: CloseAndDeleteBinding returned bad status: %d\n",
status);
return status;
}
break;
}
}
printf("ServerProvider.Disconnect: return ZX_OK\n");
return ZX_OK;
}
// Implementation of fuzzer-to-server library's `fuzzer_clean_up`.
zx_status_t CleanUp() {
if (bindings_.size() != 0) {
printf("ServerProvider.CleanUp warning: %lu unexpected dangling bindings\n",
bindings_.size());
}
while (!bindings_.empty()) {
CloseAndDeleteBinding(bindings_.begin());
}
impl_.reset(nullptr);
printf("ServerProvider.CleanUp: return ZX_OK\n");
return ZX_OK;
}
private:
// Close and delete a binding in a way that is consistent with server provider's thread model.
zx_status_t CloseAndDeleteBinding(
typename std::vector<std::unique_ptr<::fidl::Binding<Interface, Impl*>>>::iterator it) {
// Single-threaded case: Close channel and delete binding immediately.
if (!loop_.get()) {
(*it)->Close(ZX_OK);
bindings_.erase(it);
printf("ServerProvider.CloseAndDeleteBinding: Return ZX_OK\n");
return ZX_OK;
}
// Multi-threaded case: Post task to close channel and delete binding, then synchronize.
zx::event event;
zx_status_t status = zx::event::create(0, &event);
if (status != ZX_OK) {
printf("ServerProvider.CloseAndDeleteBinding: zx::event::create returned bad status: %d\n",
status);
return status;
}
async::PostTask((*it)->dispatcher(), [&]() {
(*it)->Close(ZX_OK);
status = event.signal(0, ZX_USER_SIGNAL_1);
bindings_.erase(it);
if (status != ZX_OK) {
printf(
"ServerProvider.CloseAndDeleteBinding: Channel closed signal returned bad status: "
"%d\n",
status);
printf("ServerProvider.CloseAndDeleteBinding: Fuzzer may hang indefinitely!\n");
}
});
status = event.wait_one(ZX_USER_SIGNAL_1, zx::time::infinite(), nullptr);
if (status != ZX_OK) {
printf(
"ServerProvider.CloseAndDeleteBinding: Wait for channel close signal returned bad "
"status: %d\n",
status);
return status;
}
printf("ServerProvider.CloseAndDeleteBinding: Return ZX_OK\n");
return ZX_OK;
}
std::unique_ptr<async::Loop> loop_;
std::unique_ptr<Impl> impl_;
std::vector<std::unique_ptr<::fidl::Binding<Interface, Impl*>>> bindings_;
};
} // namespace fuzzing
} // namespace fidl
// Macro for defining fuzzer-to-server library C symbols. The code will instantiate and leak a
// `ServiceProvider` that is reused between fuzz target runs. This is because
// threads/loops/dispatchers should be reused between fuzz target runs, and the fuzz target API
// contains no cleanup mechanism that triggers after all fuzz target runs are complete.
//
// Parameters:
// `ServerProvider`: The server provider class; usually fidl::fuzzing::ServerProvider.
// `Interface`: The FIDL interface class; an abstract class from fidlgen-generated code.
// `Impl`: The class that implements `Interface` to be fuzzed.
// `dispatcher_mode`: The first (and only) parameter to be passed to the `ServerProvider`
// constructor.
// `...`: Parameters to be forwarded to `ServerProvider.Init(...)`.
#define FIDL_FUZZER_DEFINITION(ServerProvider, Interface, Impl, dispatcher_mode, ...) \
\
namespace { \
\
static ServerProvider<Interface, Impl>* fuzzer_server_provider; \
} \
\
extern "C" { \
\
zx_status_t fuzzer_init() { \
if (fuzzer_server_provider == nullptr) { \
fuzzer_server_provider = new ServerProvider<Interface, Impl>(dispatcher_mode); \
} \
return fuzzer_server_provider->Init(__VA_ARGS__); \
} \
\
zx_status_t fuzzer_connect(zx_handle_t channel_handle, async_dispatcher_t* dispatcher) { \
return fuzzer_server_provider->Connect(channel_handle, dispatcher); \
} \
\
zx_status_t fuzzer_disconnect(zx_handle_t channel_handle, async_dispatcher_t* dispatcher) { \
return fuzzer_server_provider->Disconnect(channel_handle, dispatcher); \
} \
\
zx_status_t fuzzer_clean_up() { return fuzzer_server_provider->CleanUp(); } \
}
#endif // LIB_FIDL_CPP_FUZZING_SERVER_PROVIDER_H_