| // 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_ |