blob: 518b8b26dc599de664f9bd73b63f3e017ef06475 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ARBITRARY_IMPL_H_
#define FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ARBITRARY_IMPL_H_
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <optional>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
#include "absl/random/bit_gen_ref.h"
#include "absl/random/distributions.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "./fuzztest/internal/domains/absl_helpers.h"
#include "./fuzztest/internal/domains/aggregate_of_impl.h"
#include "./fuzztest/internal/domains/bit_gen_ref.h"
#include "./fuzztest/internal/domains/container_of_impl.h"
#include "./fuzztest/internal/domains/domain.h"
#include "./fuzztest/internal/domains/domain_base.h"
#include "./fuzztest/internal/domains/element_of_impl.h"
#include "./fuzztest/internal/domains/in_range_impl.h"
#include "./fuzztest/internal/domains/map_impl.h"
#include "./fuzztest/internal/domains/one_of_impl.h"
#include "./fuzztest/internal/domains/optional_of_impl.h"
#include "./fuzztest/internal/domains/smart_pointer_of_impl.h"
#include "./fuzztest/internal/domains/special_values.h"
#include "./fuzztest/internal/domains/value_mutation_helpers.h"
#include "./fuzztest/internal/domains/variant_of_impl.h"
#include "./fuzztest/internal/meta.h"
#include "./fuzztest/internal/serialization.h"
#include "./fuzztest/internal/status.h"
#include "./fuzztest/internal/table_of_recent_compares.h"
#include "./fuzztest/internal/type_support.h"
namespace fuzztest::internal {
// Fallback for error reporting, if T is not matched in Arbitrary<T>.
template <typename T, typename = void>
class ArbitraryImpl {
static_assert(always_false<T>,
"=> Type not supported yet. Consider filing an issue."
);
};
// Arbitrary for monostate.
//
// For monostate types with a default constructor, just give the single value.
template <typename T>
class ArbitraryImpl<T, std::enable_if_t<is_monostate_v<T>>>
: public domain_implementor::DomainBase<ArbitraryImpl<T>> {
public:
using typename ArbitraryImpl::DomainBase::value_type;
value_type Init(absl::BitGenRef) { return value_type{}; }
void Mutate(value_type&, absl::BitGenRef,
const domain_implementor::MutationMetadata&, bool) {}
value_type GetRandomCorpusValue(absl::BitGenRef prng) { return value_type{}; }
absl::Status ValidateCorpusValue(const value_type&) const {
return absl::OkStatus(); // Nothing to validate.
}
auto GetPrinter() const { return MonostatePrinter{}; }
};
// Arbitrary for bool.
template <>
class ArbitraryImpl<bool>
: public domain_implementor::DomainBase<ArbitraryImpl<bool>> {
public:
value_type Init(absl::BitGenRef prng) {
if (auto seed = MaybeGetRandomSeed(prng)) return *seed;
return static_cast<bool>(absl::Uniform(prng, 0, 2));
}
void Mutate(value_type& val, absl::BitGenRef,
const domain_implementor::MutationMetadata&, bool only_shrink) {
if (only_shrink) {
val = false;
} else {
val = !val;
}
}
value_type GetRandomCorpusValue(absl::BitGenRef prng) { return Init(prng); }
absl::Status ValidateCorpusValue(const value_type&) const {
return absl::OkStatus(); // Nothing to validate.
}
auto GetPrinter() const { return IntegralPrinter{}; }
};
// Arbitrary for integers.
template <typename T>
class ArbitraryImpl<T, std::enable_if_t<!std::is_const_v<T> &&
std::numeric_limits<T>::is_integer>>
: public domain_implementor::DomainBase<ArbitraryImpl<T>> {
public:
using typename ArbitraryImpl::DomainBase::value_type;
static constexpr bool is_memory_dictionary_compatible_v =
sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8;
using IntegerDictionaryT =
std::conditional_t<is_memory_dictionary_compatible_v,
IntegerDictionary<T>, bool>;
value_type Init(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
if constexpr (sizeof(T) == 1) {
return ChooseFromAll(prng);
} else {
return ChooseOneOr(SpecialValues<T>::Get(), prng,
[&] { return ChooseFromAll(prng); });
}
}
void Mutate(value_type& val, absl::BitGenRef prng,
const domain_implementor::MutationMetadata& metadata,
bool only_shrink) {
permanent_dict_candidate_ = std::nullopt;
if (only_shrink) {
if (val == 0) return;
val = ShrinkTowards(prng, val, T{0});
return;
}
const T prev = val;
do {
// Randomly apply 4 kinds of mutations with equal probabilities.
// Use permanent_dictionary_ or temporary_dictionary_ with equal
// probabilities.
if (absl::Bernoulli(prng, 0.25)) {
RandomBitFlip(prng, val, sizeof(T) * 8);
} else {
RandomWalkOrUniformOrDict<5>(
prng, val, std::numeric_limits<T>::min(),
std::numeric_limits<T>::max(), metadata.cmp_tables, temporary_dict_,
permanent_dict_, permanent_dict_candidate_);
}
// Make sure Mutate really mutates.
} while (val == prev);
}
value_type GetRandomCorpusValue(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
return ChooseFromAll(prng);
}
void UpdateMemoryDictionary(
const value_type& val, domain_implementor::ConstCmpTablesPtr cmp_tables) {
if constexpr (is_memory_dictionary_compatible_v) {
if (cmp_tables != nullptr) {
temporary_dict_.MatchEntriesFromTableOfRecentCompares(
val, *cmp_tables, std::numeric_limits<T>::min(),
std::numeric_limits<T>::max());
if (permanent_dict_candidate_.has_value() &&
permanent_dict_.Size() < kPermanentDictMaxSize) {
permanent_dict_.AddEntry(std::move(*permanent_dict_candidate_));
permanent_dict_candidate_ = std::nullopt;
}
}
}
}
absl::Status ValidateCorpusValue(const value_type&) const {
return absl::OkStatus(); // Nothing to validate.
}
auto GetPrinter() const { return IntegralPrinter{}; }
private:
static value_type ChooseFromAll(absl::BitGenRef prng) {
return absl::Uniform(absl::IntervalClosedClosed, prng,
std::numeric_limits<T>::min(),
std::numeric_limits<T>::max());
}
// Matched snapshots from table of recent compares.
// It's the "unverified" dictionary entries: the mutated
// value matched something in this snapshot, but not sure
// if it will lead to new coverage.
IntegerDictionaryT temporary_dict_;
// Set of dictionary entries from previous `temporary_dict_`
// that leads to new coverage. This is based on a heuristic
// that such entries may lead to interesting behaviors even
// after the first new coverage it triggered.
IntegerDictionaryT permanent_dict_;
std::optional<T> permanent_dict_candidate_;
static constexpr size_t kPermanentDictMaxSize = 512;
};
// Arbitrary for std::byte.
template <>
class ArbitraryImpl<std::byte>
: public domain_implementor::DomainBase<ArbitraryImpl<std::byte>> {
public:
using typename ArbitraryImpl::DomainBase::corpus_type;
using typename ArbitraryImpl::DomainBase::value_type;
value_type Init(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
return std::byte{inner_.Init(prng)};
}
void Mutate(corpus_type& val, absl::BitGenRef prng,
const domain_implementor::MutationMetadata& metadata,
bool only_shrink) {
unsigned char u8 = std::to_integer<unsigned char>(val);
inner_.Mutate(u8, prng, metadata, only_shrink);
val = std::byte{u8};
}
value_type GetRandomCorpusValue(absl::BitGenRef prng) { return Init(prng); }
absl::Status ValidateCorpusValue(const corpus_type&) const {
return absl::OkStatus(); // Nothing to validate.
}
auto GetPrinter() const { return IntegralPrinter{}; }
private:
ArbitraryImpl<unsigned char> inner_;
};
// Arbitrary for floats.
template <typename T>
class ArbitraryImpl<T, std::enable_if_t<std::is_floating_point_v<T>>>
: public domain_implementor::DomainBase<ArbitraryImpl<T>> {
public:
using typename ArbitraryImpl::DomainBase::value_type;
value_type Init(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
return ChooseOneOr(SpecialValues<T>::Get(), prng,
[&] { return absl::Uniform(prng, T{0}, T{1}); });
}
void Mutate(value_type& val, absl::BitGenRef prng,
const domain_implementor::MutationMetadata&, bool only_shrink) {
if (only_shrink) {
if (!std::isfinite(val) || val == 0) return;
val = ShrinkTowards(prng, val, T{0});
return;
}
const T prev = val;
do {
// If it is not finite we can't change it a bit because it would stay the
// same. eg inf/2 == inf.
if (!std::isfinite(val)) {
val = Init(prng);
} else {
RunOne(
prng, //
[&] { val = val / 2; }, //
[&] { val = -val; }, //
[&] { val = val + 1; }, //
[&] { val = val * 3; });
}
// Make sure Mutate really mutates.
} while (val == prev || (std::isnan(prev) && std::isnan(val)));
}
absl::Status ValidateCorpusValue(const value_type&) const {
return absl::OkStatus(); // Nothing to validate.
}
auto GetPrinter() const { return FloatingPrinter{}; }
};
// Arbitrary for containers.
template <typename T>
class ArbitraryImpl<
T,
std::enable_if_t<always_true<T>,
decltype(
// Iterable
T().begin(), T().end(), T().size(),
// Values are mutable
// This rejects associative containers, for example
// *T().begin() = std::declval<value_type_t<T>>(),
// Can insert and erase elements
T().insert(T().end(), std::declval<value_type_t<T>>()),
T().erase(T().begin()),
//
(void)0)>>
: public ContainerOfImpl<T, ArbitraryImpl<value_type_t<T>>> {};
// Arbitrary for std::string_view.
//
// We define a separate container for string_view, to detect out of bounds bugs
// better. See below.
template <typename Char>
class ArbitraryImpl<std::basic_string_view<Char>>
: public domain_implementor::DomainBase<
ArbitraryImpl<std::basic_string_view<Char>>,
std::basic_string_view<Char>,
// We use a vector to better manage the buffer and help
// ASan find out-of-bounds bugs.
std::vector<Char>> {
public:
using typename ArbitraryImpl::DomainBase::corpus_type;
using typename ArbitraryImpl::DomainBase::value_type;
corpus_type Init(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
return inner_.Init(prng);
}
void Mutate(corpus_type& val, absl::BitGenRef prng,
const domain_implementor::MutationMetadata& metadata,
bool only_shrink) {
inner_.Mutate(val, prng, metadata, only_shrink);
}
void UpdateMemoryDictionary(
const corpus_type& val,
domain_implementor::ConstCmpTablesPtr cmp_tables) {
inner_.UpdateMemoryDictionary(val, cmp_tables);
}
auto GetPrinter() const { return StringPrinter{}; }
value_type GetValue(const corpus_type& value) const {
return value_type(value.data(), value.size());
}
std::optional<corpus_type> FromValue(const value_type& value) const {
return corpus_type(value.begin(), value.end());
}
std::optional<corpus_type> ParseCorpus(const IRObject& obj) const {
return obj.ToCorpus<corpus_type>();
}
IRObject SerializeCorpus(const corpus_type& v) const {
return IRObject::FromCorpus(v);
}
absl::Status ValidateCorpusValue(const corpus_type&) const {
return absl::OkStatus(); // Nothing to validate.
}
private:
ArbitraryImpl<std::vector<Char>> inner_;
};
#ifndef ABSL_USES_STD_STRING_VIEW
// Arbitrary for absl::string_view when it does not alias to std::string_view.
//
// We define a separate container for string_view, to detect out of bounds bugs
// better. See below.
template <>
class ArbitraryImpl<absl::string_view>
: public domain_implementor::DomainBase<
ArbitraryImpl<absl::string_view>, absl::string_view,
// We use a vector to better manage the buffer and help
// ASan find out-of-bounds bugs.
std::vector<char>> {
public:
using typename ArbitraryImpl::DomainBase::corpus_type;
using typename ArbitraryImpl::DomainBase::value_type;
corpus_type Init(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
return inner_.Init(prng);
}
void Mutate(corpus_type& val, absl::BitGenRef prng,
const domain_implementor::MutationMetadata& metadata,
bool only_shrink) {
inner_.Mutate(val, prng, metadata, only_shrink);
}
void UpdateMemoryDictionary(
const corpus_type& val,
domain_implementor::ConstCmpTablesPtr cmp_tables) {
inner_.UpdateMemoryDictionary(val, cmp_tables);
}
auto GetPrinter() const { return StringPrinter{}; }
value_type GetValue(const corpus_type& value) const {
return value_type(value.data(), value.size());
}
std::optional<corpus_type> FromValue(const value_type& value) const {
return corpus_type(value.begin(), value.end());
}
std::optional<corpus_type> ParseCorpus(const IRObject& obj) const {
return obj.ToCorpus<corpus_type>();
}
IRObject SerializeCorpus(const corpus_type& v) const {
return IRObject::FromCorpus(v);
}
absl::Status ValidateCorpusValue(const corpus_type&) const {
return absl::OkStatus(); // Nothing to validate.
}
private:
ArbitraryImpl<std::vector<char>> inner_;
};
#endif
// Arbitrary for any const T.
//
// We capture const types as a workaround in order to enable
// Arbitrary<std::map<std::string, int>>() and similar container domains that
// require Arbitrary<std::pair< _const_ std::string, int>>() to be defined,
// which in turn requires Arbitrary<const std::string>() to be defined.
template <typename T>
class ArbitraryImpl<const T> : public ArbitraryImpl<T> {};
// Arbitrary for user-defined aggregate types (structs/classes).
template <typename T, typename... Elem>
AggregateOfImpl<T, RequireCustomCorpusType::kYes, ArbitraryImpl<Elem>...>
DetectAggregateOfImpl2(std::tuple<Elem&...>);
// Detect the number and types of the fields.
// TODO(sbenzaquen): Verify the compiler error in case we can't detect it and
// improve if possible.
template <typename T, int N = *DetectBindableFieldCount<T>()>
decltype(DetectAggregateOfImpl2<T>(
BindAggregate(std::declval<T&>(),
std::integral_constant<int, N>{}))) DetectAggregateOfImpl();
template <typename T>
class ArbitraryImpl<
T, std::enable_if_t<std::is_class_v<T> && std::is_aggregate_v<T> &&
// Monostates have their own domain.
!is_monostate_v<T> &&
// std::array uses the Tuple domain.
!is_array_v<T> &&
// Flatbuffers tables have their own domain.
!is_flatbuffers_table_v<T>>>
: public decltype(DetectAggregateOfImpl<T>()) {};
// Arbitrary for std::pair.
template <typename T, typename U>
class ArbitraryImpl<std::pair<T, U>>
: public AggregateOfImpl<
std::pair<std::remove_const_t<T>, std::remove_const_t<U>>,
std::is_const_v<T> || std::is_const_v<U>
? RequireCustomCorpusType::kYes
: RequireCustomCorpusType::kNo,
ArbitraryImpl<T>, ArbitraryImpl<U>> {};
// Arbitrary for std::tuple.
template <typename... T>
class ArbitraryImpl<std::tuple<T...>, std::enable_if_t<sizeof...(T) != 0>>
: public AggregateOfImpl<std::tuple<T...>, RequireCustomCorpusType::kNo,
ArbitraryImpl<T>...> {};
// Arbitrary for std::array.
template <typename T, size_t N>
auto AggregateOfImplForArray() {
return ApplyIndex<N>([&](auto... I) {
return AggregateOfImpl<std::array<T, N>, RequireCustomCorpusType::kNo,
std::enable_if_t<(I >= 0), ArbitraryImpl<T>>...>{};
});
}
template <typename T, size_t N>
class ArbitraryImpl<std::array<T, N>>
: public decltype(AggregateOfImplForArray<T, N>()) {};
// Arbitrary for std::variant.
template <typename... T>
class ArbitraryImpl<std::variant<T...>>
: public VariantOfImpl<std::variant<T...>, ArbitraryImpl<T>...> {};
// Arbitrary for std::optional.
template <typename T>
class ArbitraryImpl<std::optional<T>>
: public OptionalOfImpl<std::optional<T>> {
public:
ArbitraryImpl() : ArbitraryImpl::OptionalOfImpl(ArbitraryImpl<T>()) {}
};
// Used by Arbitrary std::unique_ptr / std::shared_ptr.
template <typename T>
const Domain<T>& GetGlobalDomainDefaultInstance() {
static const auto* instance = new Domain<T>(ArbitraryImpl<T>());
return *instance;
}
// Arbitrary for std::unique_ptr.
template <typename T>
class ArbitraryImpl<std::unique_ptr<T>>
: public SmartPointerOfImpl<std::unique_ptr<T>> {
public:
ArbitraryImpl()
: ArbitraryImpl::SmartPointerOfImpl(GetGlobalDomainDefaultInstance) {}
};
// Arbitrary for std::shared_ptr.
template <typename T>
class ArbitraryImpl<std::shared_ptr<T>>
: public SmartPointerOfImpl<std::shared_ptr<T>> {
public:
ArbitraryImpl()
: ArbitraryImpl::SmartPointerOfImpl(GetGlobalDomainDefaultInstance) {}
};
// Arbitrary for absl::Duration.
template <>
class ArbitraryImpl<absl::Duration>
: public OneOfImpl<
ElementOfImpl<absl::Duration>,
ReversibleMapImpl<absl::Duration (*)(int64_t, uint32_t),
std::optional<std::tuple<int64_t, uint32_t>> (*)(
absl::Duration),
ArbitraryImpl<int64_t>, InRangeImpl<uint32_t>>> {
public:
ArbitraryImpl()
: OneOfImpl(
ElementOfImpl<absl::Duration>(
{absl::InfiniteDuration(), -absl::InfiniteDuration()}),
ReversibleMapImpl<absl::Duration (*)(int64_t, uint32_t),
std::optional<std::tuple<int64_t, uint32_t>> (*)(
absl::Duration),
ArbitraryImpl<int64_t>, InRangeImpl<uint32_t>>(
[](int64_t secs, uint32_t ticks) {
return MakeDuration(secs, ticks);
},
[](absl::Duration duration) {
auto [secs, ticks] = GetSecondsAndTicks(duration);
return std::optional{std::tuple{secs, ticks}};
},
ArbitraryImpl<int64_t>(),
// ticks is 1/4 of a nanosecond and has a range of [0, 4B - 1]
InRangeImpl<uint32_t>(0u, 3'999'999'999u))) {}
};
// Arbitrary for absl::Time.
template <>
class ArbitraryImpl<absl::Time>
: public ReversibleMapImpl<absl::Time (*)(absl::Duration),
std::optional<std::tuple<absl::Duration>> (*)(
absl::Time),
ArbitraryImpl<absl::Duration>> {
public:
ArbitraryImpl()
: ReversibleMapImpl<absl::Time (*)(absl::Duration),
std::optional<std::tuple<absl::Duration>> (*)(
absl::Time),
ArbitraryImpl<absl::Duration>>(
[](absl::Duration duration) {
return absl::UnixEpoch() + duration;
},
[](absl::Time time) {
return std::optional{std::tuple{time - absl::UnixEpoch()}};
},
ArbitraryImpl<absl::Duration>()) {}
};
// Arbitrary for absl::BitGenRef.
template <>
class ArbitraryImpl<absl::BitGenRef>
: public BitGenRefDomain<
/*Tuple=*/
AggregateOfImpl<
std::tuple<std::vector<uint8_t>, std::vector<uint8_t>, uint64_t>,
internal::RequireCustomCorpusType::kNo,
/*DataSequence=*/
SequenceContainerOfImpl<std::vector<uint8_t>,
ArbitraryImpl<uint8_t>>,
/*ControlSequence=*/
SequenceContainerOfImpl<std::vector<uint8_t>,
ElementOfImpl<uint8_t>>,
/*Seed=*/ElementOfImpl<uint64_t> /**/
>> {
using ValueType =
std::tuple<std::vector<uint8_t>, std::vector<uint8_t>, uint64_t>;
// The control stream determines the behavior of the FuzzingBitGen;
// See the implementation for valid value, which may change.
using DataSequence =
SequenceContainerOfImpl<std::vector<uint8_t>, ArbitraryImpl<uint8_t>>;
using ControlSequence =
SequenceContainerOfImpl<std::vector<uint8_t>, ElementOfImpl<uint8_t>>;
// NOTE: The seed could be an ArbitraryImpl<uint64_t>.
using Seed = ElementOfImpl<uint64_t>;
using Tuple =
AggregateOfImpl<ValueType, internal::RequireCustomCorpusType::kNo,
DataSequence, ControlSequence, Seed>;
public:
ArbitraryImpl()
: BitGenRefDomain(
Tuple(std::in_place, DataSequence{},
ControlSequence{ElementOfImpl<uint8_t>({0, 1, 2, 3, 4, 5})},
Seed({0}))) {}
};
} // namespace fuzztest::internal
#endif // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ARBITRARY_IMPL_H_