blob: e10187c608ef3a9cd54b2778914ff13564e4192a [file]
// 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/time/time.h"
#include "./fuzztest/internal/coverage.h"
#include "./fuzztest/internal/domains/absl_helpers.h"
#include "./fuzztest/internal/domains/aggregate_of_impl.h"
#include "./fuzztest/internal/domains/container_of_impl.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/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/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 DomainBase<ArbitraryImpl<T>> {
public:
using typename ArbitraryImpl::DomainBase::value_type;
value_type Init(absl::BitGenRef) { return value_type{}; }
void Mutate(value_type&, absl::BitGenRef, bool) {}
auto GetPrinter() const { return MonostatePrinter{}; }
};
// Arbitrary for bool.
template <>
class ArbitraryImpl<bool> : public DomainBase<ArbitraryImpl<bool>> {
public:
value_type Init(absl::BitGenRef prng) {
return static_cast<bool>(absl::Uniform(prng, 0, 2));
}
void Mutate(value_type& val, absl::BitGenRef, bool only_shrink) {
if (only_shrink) {
val = false;
} else {
val = !val;
}
}
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 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) {
const auto choose_from_all = [&] {
return absl::Uniform(absl::IntervalClosedClosed, prng,
std::numeric_limits<T>::min(),
std::numeric_limits<T>::max());
};
if constexpr (sizeof(T) == 1) {
return choose_from_all();
} else {
static constexpr T special[] = {
T{0}, T{1},
// For some types, ~T{} is promoted to int. Convert back to T.
static_cast<T>(~T{}),
std::numeric_limits<T>::is_signed
? std::numeric_limits<T>::max()
: std::numeric_limits<T>::max() >> 1};
return ChooseOneOr(special, prng, choose_from_all);
}
}
void Mutate(value_type& val, absl::BitGenRef prng, 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(),
temporary_dict_, permanent_dict_,
permanent_dict_candidate_);
}
// Make sure Mutate really mutates.
} while (val == prev);
}
void UpdateMemoryDictionary(const value_type& val) {
if constexpr (is_memory_dictionary_compatible_v) {
if (GetExecutionCoverage() != nullptr) {
temporary_dict_.MatchEntriesFromTableOfRecentCompares(
val, GetExecutionCoverage()->GetTablesOfRecentCompares(),
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;
}
}
}
}
auto GetPrinter() const { return IntegralPrinter{}; }
private:
// 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 floats.
template <typename T>
class ArbitraryImpl<T, std::enable_if_t<std::is_floating_point_v<T>>>
: public DomainBase<ArbitraryImpl<T>> {
public:
using typename ArbitraryImpl::DomainBase::value_type;
value_type Init(absl::BitGenRef prng) {
const T special[] = {
T{0.0}, T{-0.0}, T{1.0}, T{-1.0}, std::numeric_limits<T>::max(),
std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(),
// std::nan is double. Cast to T explicitly.
static_cast<T>(std::nan(""))};
return ChooseOneOr(special, prng,
[&] { return absl::Uniform(prng, T{0}, T{1}); });
}
void Mutate(value_type& val, absl::BitGenRef prng, 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)));
}
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 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) { return inner_.Init(prng); }
void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) {
inner_.Mutate(val, prng, only_shrink);
}
void UpdateMemoryDictionary(const corpus_type& val) {
inner_.UpdateMemoryDictionary(val);
}
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);
}
private:
ArbitraryImpl<std::vector<Char>> inner_;
};
// 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>>>
: 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>, ArbitraryImpl<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>, ArbitraryImpl<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>, ArbitraryImpl<T>> {
public:
ArbitraryImpl()
: ArbitraryImpl::SmartPointerOfImpl(GetGlobalDomainDefaultInstance) {}
};
// Arbitrary for absl::Duration.
template <>
class ArbitraryImpl<absl::Duration>
: public OneOfImpl<ElementOfImpl<absl::Duration>,
MapImpl<absl::Duration (*)(int64_t, uint32_t),
ArbitraryImpl<int64_t>, InRangeImpl<uint32_t>>> {
public:
ArbitraryImpl()
: OneOfImpl(
ElementOfImpl<absl::Duration>(
{absl::InfiniteDuration(), -absl::InfiniteDuration()}),
MapImpl<absl::Duration (*)(int64_t, uint32_t),
ArbitraryImpl<int64_t>, InRangeImpl<uint32_t>>(
[](int64_t secs, uint32_t ticks) {
return MakeDuration(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 MapImpl<absl::Time (*)(absl::Duration),
ArbitraryImpl<absl::Duration>> {
public:
ArbitraryImpl()
: MapImpl<absl::Time (*)(absl::Duration), ArbitraryImpl<absl::Duration>>(
[](absl::Duration duration) {
return absl::UnixEpoch() + duration;
},
ArbitraryImpl<absl::Duration>()) {}
};
} // namespace fuzztest::internal
#endif // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ARBITRARY_IMPL_H_