| // 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_FUZZING_CPP_TRAITS_H_ | |
| #define LIB_FUZZING_CPP_TRAITS_H_ | |
| #include <lib/fidl/cpp/interface_request.h> | |
| #include <lib/fuzzing/cpp/fuzz_input.h> | |
| #include <lib/zx/object.h> | |
| #include <zircon/assert.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/bti.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/channel.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/debuglog.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/event.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/eventpair.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/exception.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/fifo.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/guest.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/interrupt.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/iommu.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/pager.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/pmt.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/port.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/profile.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/resource.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/socket.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/suspend_token.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/timer.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/vcpu.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/vmar.h> | |
| #include <zircon/system/ulib/zx/include/lib/zx/vmo.h> | |
| #include <zircon/types.h> | |
| #include <array> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| namespace fuzzing { | |
| // Abstract type traits for low-level types needed by FIDL fuzzers: | |
| // | |
| // size_t MinSize<T>() | |
| // Returns the minimum size, in bytes, of fuzz input data consumed by an | |
| // instance of type, T. | |
| // | |
| // T Allocate<T>(FuzzInput* src, size_t* size) | |
| // Allocates at most |size| bytes from |src| to return a fuzzer input of type, | |
| // T. Modifies |size| to reflect actual number of bytes consumed from |src|. | |
| // Both |src| and |size| are assumed to be non-null. | |
| template <typename T> | |
| struct MinSize { | |
| static_assert(sizeof(T) == -1, "Missing fidl::fuzzing::MinSize<T> specialization"); | |
| constexpr operator size_t() const { return 0; } | |
| }; | |
| template <typename T> | |
| struct Allocate { | |
| static_assert(sizeof(T) == -1, "Missing fidl::fuzzing::Allocate<T> specialization"); | |
| T operator()(FuzzInput* src, size_t* size) { | |
| *size = 0; | |
| return T(); | |
| } | |
| }; | |
| // Statically sized (primitive) traits operate as follows: | |
| // 1. MinSize<T> = sizeof(T); | |
| // 2. Allocate<T>(src, size) asserts that |size| >= sizeof(T); | |
| // 3. Allocate<T>(src, size) asserts that taking sufficient bytes for return | |
| // succeeds. | |
| // 4. Allocate<T>(src, size) takes up to |size| bytes to produce a new fuzzer | |
| // input of type, T. | |
| #define FUZZING_STATIC(T) \ | |
| template <> \ | |
| struct MinSize<T> { \ | |
| constexpr operator size_t() const { return sizeof(T); } \ | |
| }; \ | |
| template <> \ | |
| struct Allocate<T> { \ | |
| T operator()(FuzzInput* src, size_t* size) { \ | |
| T out; \ | |
| ZX_ASSERT(*size >= sizeof(T)); \ | |
| *size = sizeof(T); \ | |
| ZX_ASSERT(src->CopyObject(&out)); \ | |
| return out; \ | |
| } \ | |
| } | |
| // TODO(fxbug.dev/25053): Use a more useful distribution for types such as bool. | |
| FUZZING_STATIC(bool); | |
| FUZZING_STATIC(uint8_t); | |
| FUZZING_STATIC(uint16_t); | |
| FUZZING_STATIC(uint32_t); | |
| FUZZING_STATIC(uint64_t); | |
| FUZZING_STATIC(int8_t); | |
| FUZZING_STATIC(int16_t); | |
| FUZZING_STATIC(int32_t); | |
| FUZZING_STATIC(int64_t); | |
| FUZZING_STATIC(float); | |
| FUZZING_STATIC(double); | |
| #undef FUZZING_STATIC | |
| // Handle traits: | |
| // Like FUZZING_STATIC(zx_handle_t), but return ::zx::object<T> instance. | |
| // | |
| // TODO(fxbug.dev/25053): Generate a distribution of legitimate and illegitimate handles. | |
| template <typename T> | |
| struct MinSize<::zx::object<T>> { | |
| constexpr operator size_t() const { return sizeof(zx_handle_t); } | |
| }; | |
| template <typename T> | |
| struct Allocate<::zx::object<T>> { | |
| ::zx::object<T> operator()(FuzzInput* src, size_t* size) { | |
| zx_handle_t handle; | |
| ZX_ASSERT(*size >= sizeof(zx_handle_t)); | |
| *size = sizeof(zx_handle_t); | |
| ZX_ASSERT(src->CopyObject(&handle)); | |
| return ::zx::object<T>(handle); | |
| } | |
| }; | |
| // Statically sized zircon object traits delegate to ::zx::object<T> traits. | |
| #define FUZZING_ZX_OBJ(T) \ | |
| template <> \ | |
| struct MinSize<T> { \ | |
| constexpr operator size_t() const { return MinSize<::zx::object<T>>(); } \ | |
| }; \ | |
| template <> \ | |
| struct Allocate<T> { \ | |
| T operator()(FuzzInput* src, size_t* size) { \ | |
| return T(Allocate<zx::object<T>>{}(src, size).get()); \ | |
| } \ | |
| } | |
| FUZZING_ZX_OBJ(::zx::bti); | |
| FUZZING_ZX_OBJ(::zx::channel); | |
| FUZZING_ZX_OBJ(::zx::debuglog); | |
| FUZZING_ZX_OBJ(::zx::event); | |
| FUZZING_ZX_OBJ(::zx::eventpair); | |
| FUZZING_ZX_OBJ(::zx::exception); | |
| FUZZING_ZX_OBJ(::zx::fifo); | |
| FUZZING_ZX_OBJ(::zx::guest); | |
| FUZZING_ZX_OBJ(::zx::interrupt); | |
| FUZZING_ZX_OBJ(::zx::iommu); | |
| FUZZING_ZX_OBJ(::zx::pager); | |
| FUZZING_ZX_OBJ(::zx::pmt); | |
| FUZZING_ZX_OBJ(::zx::port); | |
| FUZZING_ZX_OBJ(::zx::profile); | |
| FUZZING_ZX_OBJ(::zx::resource); | |
| FUZZING_ZX_OBJ(::zx::socket); | |
| FUZZING_ZX_OBJ(::zx::suspend_token); | |
| FUZZING_ZX_OBJ(::zx::timer); | |
| FUZZING_ZX_OBJ(::zx::vcpu); | |
| FUZZING_ZX_OBJ(::zx::vmar); | |
| FUZZING_ZX_OBJ(::zx::vmo); | |
| #undef FUZZING_ZX_OBJ | |
| template <typename T> | |
| struct MinSize<::fidl::InterfaceRequest<T>> { | |
| constexpr operator size_t() const { return MinSize<::zx::channel>(); } | |
| }; | |
| template <typename T> | |
| struct Allocate<::fidl::InterfaceRequest<T>> { | |
| ::fidl::InterfaceRequest<T> operator()(FuzzInput* src, size_t* size) { | |
| return ::fidl::InterfaceRequest<T>(std::move(Allocate<zx::channel>{}(src, size))); | |
| } | |
| }; | |
| // String traits: | |
| // MinSize is 0; take bytes as |const char*| to back |size|-sized string. | |
| template <> | |
| struct MinSize<std::string> { | |
| constexpr operator size_t() const { return 0; } | |
| }; | |
| template <> | |
| struct Allocate<std::string> { | |
| std::string operator()(FuzzInput* src, size_t* size) { | |
| if (*size == 0) { | |
| return std::string(); | |
| } | |
| const char* out = reinterpret_cast<const char*>(src->TakeBytes(*size)); | |
| return std::string(out, *size); | |
| } | |
| }; | |
| // Array traits: | |
| // MinSize is 0 (i.e., admit empty portion of array); take bytes for up to S | |
| // instances of T. If |size| > S * MinSize<T>(), attempt to evenly distribute | |
| // slack bytes amongst T-instance allocations. The purpose of this distribution | |
| // is to provide data to variable-sized types that may be stored in the array. | |
| // | |
| // Caveat: When MinSize<T>() = 0, attempt to allocate S T-instances. | |
| // | |
| // TODO(fxbug.dev/25053): Consume some input bytes to allocate pseudorandom number of items. | |
| template <typename T, size_t S> | |
| struct MinSize<std::array<T, S>> { | |
| constexpr operator size_t() const { return 0; } | |
| }; | |
| template <typename T, size_t S> | |
| struct Allocate<std::array<T, S>> { | |
| std::array<T, S> operator()(FuzzInput* src, size_t* size) { | |
| if (S == 0 || *size == 0 || *size < MinSize<T>()) { | |
| *size = 0; | |
| return std::array<T, S>(); | |
| } | |
| const size_t requested_size = *size; | |
| const size_t item_size = Allocate<std::array<T, S>>::item_size(); | |
| size_t num; | |
| size_t slack_per_item; | |
| if (item_size == 0) { | |
| // Special case: Items are variable-sized objects that may be empty | |
| // (e.g., vectors). | |
| // Attempt to allocate S items. | |
| num = S; | |
| slack_per_item = requested_size / num; | |
| } else { | |
| // Items are of non-zero size. | |
| const size_t size_num = requested_size / item_size; | |
| num = size_num <= S ? size_num : S; | |
| const size_t slack = requested_size - (num * item_size); | |
| slack_per_item = num == 0 ? 0 : slack / num; | |
| } | |
| std::array<T, S> out; | |
| *size = 0; | |
| for (size_t i = 0; i < num; i++) { | |
| size_t current_item_size = item_size + slack_per_item; | |
| out[i] = Allocate<T>{}(src, ¤t_item_size); | |
| *size += current_item_size; | |
| } | |
| return out; | |
| } | |
| private: | |
| static size_t item_size() { return MinSize<T>(); } | |
| }; | |
| // Vector traits: | |
| // MinSize is 0 (i.e., admit empty vector); take MinSize<T>()-byte chunks from | |
| // |src| for constructing instances of T. Allocating larger-than-min-size | |
| // T-instances is currently unsupported. | |
| // | |
| // Caveat: When MinSize<T>() = 0, treat T-instances as though they will | |
| // allocate 8 bytes, enough for a 64-bit pointer. | |
| // | |
| // TODO(fxbug.dev/25053): Consume some input bytes to allocate pseudorandom number of items. | |
| template <typename T> | |
| struct MinSize<std::vector<T>> { | |
| constexpr operator size_t() const { return 0; } | |
| }; | |
| template <typename T> | |
| struct Allocate<std::vector<T>> { | |
| std::vector<T> operator()(FuzzInput* src, size_t* size) { | |
| const size_t item_size = | |
| Allocate<std::vector<T>>::item_size() == 0 ? 8 : Allocate<std::vector<T>>::item_size(); | |
| if (*size == 0 || *size < item_size) { | |
| *size = 0; | |
| return std::vector<T>(); | |
| } | |
| const size_t requested_size = *size; | |
| const size_t num = requested_size / item_size; | |
| const size_t slack = requested_size - (num * item_size); | |
| const size_t slack_per_item = num == 0 ? 0 : slack / num; | |
| std::vector<T> v; | |
| *size = 0; | |
| for (size_t i = 0; i < num; i++) { | |
| size_t current_item_size = item_size + slack_per_item; | |
| v.push_back(Allocate<T>{}(src, ¤t_item_size)); | |
| *size += current_item_size; | |
| } | |
| return v; | |
| } | |
| private: | |
| static size_t item_size() { return MinSize<T>(); } | |
| }; | |
| // Pointer traits: | |
| // Min*size = 0 (i.e., support nullptr); when |size| >= MinSize<T>(), take bytes | |
| // to allocate a non-null std::unique_ptr<T>; otherwise return nullptr. | |
| template <typename T> | |
| struct MinSize<std::unique_ptr<T>> { | |
| constexpr operator size_t() const { return 0; } | |
| }; | |
| template <typename T> | |
| struct Allocate<std::unique_ptr<T>> { | |
| std::unique_ptr<T> operator()(FuzzInput* src, size_t* size) { | |
| if (*size < MinSize<T>()) { | |
| *size = 0; | |
| return std::unique_ptr<T>(); | |
| } | |
| return std::make_unique<T>(Allocate<T>{}(src, size)); | |
| } | |
| }; | |
| } // namespace fuzzing | |
| #endif // LIB_FUZZING_CPP_TRAITS_H_ |