blob: 4c920cbebf830eb3b1137f02e8f3c99c8285086c [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_FUZZER_H_
#define LIB_FIDL_CPP_FUZZING_FUZZER_H_
#include <lib/async/dispatcher.h>
#include <lib/fidl/cpp/interface_ptr.h>
#include <lib/zx/clock.h>
#include <lib/zx/event.h>
#include <lib/zx/time.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <utility>
#ifdef __cplusplus
extern "C" {
#endif
zx_status_t fuzzer_init();
zx_status_t fuzzer_connect(zx_handle_t, async_dispatcher_t*);
zx_status_t fuzzer_disconnect(zx_handle_t, async_dispatcher_t*);
zx_status_t fuzzer_clean_up();
#ifdef __cplusplus
}
#endif
namespace fidl {
namespace fuzzing {
// Helper class for signalling when a callback completes.
//
// Use `zx::unowned_event` because `Fuzzer` that owns the event may go out of scope before callback
// is invoked.
class FuzzerCallbackSignaller {
public:
explicit FuzzerCallbackSignaller(const zx::event& evt) : evt_(evt) {}
// Invoked from within method callback to signal that callback has run. Note that `signal()` may
// return non-OK status if, for example, fuzzer timed out waiting for callback.
zx_status_t SignalCallback() const { return evt_->signal(0, ZX_USER_SIGNAL_7); }
private:
zx::unowned_event evt_;
};
// Helper class for code generated by the `cpp_libfuzzer` FIDL compiler backend.
//
// This class implements setup, bind, and teardown for a single pass of a fuzzer generated using the
// `cpp_libfuzzer` FIDL compiler backend. It _always_ owns `evt_` handle. It _sometimes_ owns the
// `client_handle_` and `service_handle_`; these are stored as raw handles because one of them is
// passed to an opaque service provider via a C interface.
template <typename Impl>
class Fuzzer {
public:
Fuzzer(async_dispatcher_t* dispatcher)
: init_status_(ZX_ERR_UNAVAILABLE),
service_status_(ZX_ERR_UNAVAILABLE),
client_status_(ZX_ERR_UNAVAILABLE),
service_handle_(ZX_HANDLE_INVALID),
client_handle_(ZX_HANDLE_INVALID),
dispatcher_(dispatcher) {}
// Attempt to initialize the fuzzer by allowing the service provider to initialize itself and
// creating kernel primitives.
zx_status_t Init() {
if ((init_status_ = fuzzer_init()) != ZX_OK) {
return init_status_;
}
if ((init_status_ = zx_channel_create(0, &service_handle_, &client_handle_)) != ZX_OK) {
return init_status_;
}
if ((init_status_ = zx::event::create(0, &evt_)) != ZX_OK) {
return init_status_;
}
return init_status_;
}
// Attempt to pass `service_handle_` to service provider. If successful, the service
// implementation now owns `service_handle_`.
zx_status_t BindService() {
service_status_ = fuzzer_connect(service_handle_, dispatcher_);
return service_status_;
}
// Attempt to initialize client `InterfacePtr`. If successful, the `InterfacePtr` now owns
// `client_handle_`.
zx_status_t BindClient(InterfacePtr<Impl>* iface, async_dispatcher_t* dispatcher) {
zx::channel client_channel(client_handle_);
client_status_ = iface->Bind(std::move(client_channel), dispatcher);
return client_status_;
}
// Produce a callback signaller that may outlive this Fuzzer.
FuzzerCallbackSignaller NewCallbackSignaller() { return FuzzerCallbackSignaller(evt_); }
// Invoked from main fuzzer thread to wait for callback to run.
// TODO(markdittmer): Make deadline configurable.
zx_status_t WaitForCallback() const {
zx_signals_t pending;
zx::time deadline = zx::clock::get_monotonic() + zx::duration(5000000000);
return evt_.wait_one(ZX_USER_SIGNAL_7, deadline, &pending);
}
~Fuzzer() {
// Fuzzer owns client_handle_ when client setup failed.
if (client_status_ != ZX_OK) {
zx_handle_close(client_handle_);
}
// Service owns service_handle_ when service setup succeeded, else Fuzzer owns it.
if (service_status_ == ZX_OK) {
fuzzer_disconnect(service_handle_, dispatcher_);
} else {
zx_handle_close(service_handle_);
}
// Clean up only when init succeeded.
if (init_status_ == ZX_OK) {
fuzzer_clean_up();
}
}
private:
zx_status_t init_status_;
zx_status_t service_status_;
zx_status_t client_status_;
zx_handle_t service_handle_;
zx_handle_t client_handle_;
zx::event evt_;
async_dispatcher_t* dispatcher_;
};
} // namespace fuzzing
} // namespace fidl
#endif // LIB_FIDL_CPP_FUZZING_FUZZER_H_