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