diff --git a/centipede/BUILD b/centipede/BUILD index 5cb6d5bf..c052da9f 100644 --- a/centipede/BUILD +++ b/centipede/BUILD @@ -929,8 +929,8 @@ cc_library( "@com_google_absl//absl/random", "@com_google_absl//absl/types:span", "@com_google_fuzztest//common:defs", - "@com_google_fuzztest//fuzztest:coverage", "@com_google_fuzztest//fuzztest:domain_core", + "@com_google_fuzztest//fuzztest:table_of_recent_compares", ], ) diff --git a/centipede/fuzztest_mutator.cc b/centipede/fuzztest_mutator.cc index fc85d362..3ea290d5 100644 --- a/centipede/fuzztest_mutator.cc +++ b/centipede/fuzztest_mutator.cc @@ -29,7 +29,7 @@ #include "./centipede/mutation_input.h" #include "./common/defs.h" #include "./fuzztest/domain_core.h" -#include "./fuzztest/internal/coverage.h" +#include "./fuzztest/internal/table_of_recent_compares.h" namespace centipede { @@ -40,30 +40,25 @@ using MutatorDomainBase = } // namespace +struct FuzzTestMutator::MutationMetadata { + fuzztest::internal::TablesOfRecentCompares cmp_tables; +}; + class FuzzTestMutator::MutatorDomain : public MutatorDomainBase { public: MutatorDomain() : MutatorDomainBase(fuzztest::VectorOf(fuzztest::Arbitrary())) { - if (fuzztest::internal::GetExecutionCoverage() == nullptr) { - execution_coverage_ = std::make_unique( - /*counter_map=*/absl::Span{}); - execution_coverage_->SetIsTracing(true); - fuzztest::internal::SetExecutionCoverage(execution_coverage_.get()); - } } ~MutatorDomain() { - if (fuzztest::internal::GetExecutionCoverage() == execution_coverage_.get()) - fuzztest::internal::SetExecutionCoverage(nullptr); } - - private: - using ExecutionCoverage = fuzztest::internal::ExecutionCoverage; - std::unique_ptr execution_coverage_; }; FuzzTestMutator::FuzzTestMutator(const Knobs &knobs, uint64_t seed) - : knobs_(knobs), prng_(seed), domain_(std::make_unique()) { + : knobs_(knobs), + prng_(seed), + mutation_metadata_(std::make_unique()), + domain_(std::make_unique()) { domain_->WithMinSize(1).WithMaxSize(max_len_); } @@ -123,23 +118,23 @@ void FuzzTestMutator::MutateMany(const std::vector& inputs, inputs[absl::Uniform(prng_, 0, inputs.size())].data; CrossOver(mutant, other_input); } else { - domain_->Mutate(mutant, prng_, /*only_shrink=*/false); + domain_->Mutate(mutant, prng_, + {.cmp_tables = &mutation_metadata_->cmp_tables}, + /*only_shrink=*/false); } mutants.push_back(std::move(mutant)); } } void FuzzTestMutator::SetMetadata(const ExecutionMetadata& metadata) { - metadata.ForEachCmpEntry([](ByteSpan a, ByteSpan b) { + metadata.ForEachCmpEntry([this](ByteSpan a, ByteSpan b) { size_t size = a.size(); if (size < kMinCmpEntrySize) return; if (size > kMaxCmpEntrySize) return; // Use the memcmp table to avoid subtlety of the container domain mutation // with integer tables. E.g. it won't insert integer comparison data. - fuzztest::internal::GetExecutionCoverage() - ->GetTablesOfRecentCompares() - .GetMutable<0>() - .Insert(a.data(), b.data(), size); + mutation_metadata_->cmp_tables.GetMutable<0>().Insert(a.data(), b.data(), + size); }); } diff --git a/centipede/fuzztest_mutator.h b/centipede/fuzztest_mutator.h index 3afa48bb..b9c81618 100644 --- a/centipede/fuzztest_mutator.h +++ b/centipede/fuzztest_mutator.h @@ -54,6 +54,7 @@ class FuzzTestMutator { // TODO(xinhaoyuan): Support set_alignment(). private: + class MutationMetadata; class MutatorDomain; // Propagates the execution `metadata` to the internal mutation dictionary. @@ -72,6 +73,8 @@ class FuzzTestMutator { const Knobs &knobs_; Rng prng_; size_t max_len_ = 1000; + + std::unique_ptr mutation_metadata_; std::unique_ptr domain_; }; diff --git a/domain_tests/BUILD b/domain_tests/BUILD index 8b0dc9b7..2d882741 100644 --- a/domain_tests/BUILD +++ b/domain_tests/BUILD @@ -92,8 +92,8 @@ cc_test( "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/random", - "@com_google_fuzztest//fuzztest:coverage", "@com_google_fuzztest//fuzztest:domain_core", + "@com_google_fuzztest//fuzztest:table_of_recent_compares", "@com_google_fuzztest//fuzztest:type_support", "@com_google_googletest//:gtest_main", ], @@ -111,6 +111,7 @@ cc_library( "@com_google_absl//absl/random:bit_gen_ref", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", + "@com_google_fuzztest//fuzztest:domain_core", "@com_google_fuzztest//fuzztest:logging", "@com_google_fuzztest//fuzztest:meta", "@com_google_fuzztest//fuzztest:serialization", diff --git a/domain_tests/CMakeLists.txt b/domain_tests/CMakeLists.txt index 3ba1e94b..8900ccc4 100644 --- a/domain_tests/CMakeLists.txt +++ b/domain_tests/CMakeLists.txt @@ -82,8 +82,8 @@ fuzztest_cc_test( absl::flat_hash_map absl::flat_hash_set absl::random_random - fuzztest::coverage fuzztest::domain_core + fuzztest::table_of_recent_compares fuzztest::type_support GTest::gmock_main ) @@ -100,6 +100,7 @@ fuzztest_cc_library( absl::random_bit_gen_ref absl::status absl::strings + fuzztest::domain_core fuzztest::logging fuzztest::meta fuzztest::serialization diff --git a/domain_tests/aggregate_combinators_test.cc b/domain_tests/aggregate_combinators_test.cc index a99d5581..07d12e7f 100644 --- a/domain_tests/aggregate_combinators_test.cc +++ b/domain_tests/aggregate_combinators_test.cc @@ -82,7 +82,7 @@ TEST(StructOf, MutateGeneratesValidValues) { Set field_s; Value agg(domain, bitgen); while (field_a.size() < 2 && field_s.size() < 10) { - agg.Mutate(domain, bitgen, false); + agg.Mutate(domain, bitgen, {}, false); EXPECT_THAT(agg.user_value.a, AnyOf(5, 10)); field_a.insert(agg.user_value.a); field_s.insert(agg.user_value.s); @@ -104,11 +104,11 @@ TEST(StructOf, WorksWithStructWithUpTo16Fields) { Value value(domain, bitgen); const uint32_t initial_sum = value.user_value.sum(); while (value.user_value.sum() == initial_sum) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); } // Check that shrinking works too. while (value.user_value.sum() != 0) { - value.Mutate(domain, bitgen, true); + value.Mutate(domain, bitgen, {}, true); } } @@ -151,7 +151,7 @@ TEST(ConstructorOf, MutateGeneratesValidValues) { Set field_s; Value agg(domain, bitgen); while (field_a.size() < 2 && field_d.size() < 10 && field_s.size() < 10) { - agg.Mutate(domain, bitgen, false); + agg.Mutate(domain, bitgen, {}, false); EXPECT_THAT(agg.user_value.a(), AnyOf(5, 10)); field_a.insert(agg.user_value.a()); field_d.insert(agg.user_value.d()); @@ -181,11 +181,11 @@ TEST(ConstructorOf, WorksWithTemplatedConstructors) { Value value(domain, bitgen); const uint32_t initial_sum = value.user_value.sum(); while (value.user_value.sum() == initial_sum) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); } // Check that shrinking works too. while (value.user_value.sum() != 0) { - value.Mutate(domain, bitgen, true); + value.Mutate(domain, bitgen, {}, true); } } @@ -201,7 +201,7 @@ TEST(ConstructorOf, WorksWithMoveConstructors) { Value value(domain, bitgen); const std::string initial_value = value.user_value.data_; while (value.user_value.data_ == initial_value) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); } } @@ -254,7 +254,7 @@ TEST(VariantOf, MutateGenerateValidValues) { Value value(domain, bitgen); while (unique_values[0].size() < 2 || unique_values[1].size() < n || counter[2] < n || counter[3] < n) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); if (value.user_value.index() == 0) { int val = std::get<0>(value.user_value); EXPECT_THAT(val, AnyOf(5, 10)); @@ -325,7 +325,7 @@ TEST(OptionalOf, MutateCanMakeValuesOrNull) { absl::BitGen bitgen; Value value(domain, bitgen); while (values.size() < 4) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); values.insert(value.user_value); } EXPECT_THAT(values, UnorderedElementsAre(std::nullopt, Optional(1), @@ -347,7 +347,7 @@ TEST(OptionalOf, AlwaysGenerateNulloptWhenPolicySet) { Value value(domain, bitgen); for (int i = 0; i < 10000; ++i) { EXPECT_TRUE(value == std::nullopt) << "Didn't expect non-null value!"; - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); } } @@ -359,7 +359,7 @@ TEST(OptionalOf, DoesntGenerateNulloptWhenPolicySet) { Value value(domain, bitgen); for (int i = 0; i < 10000; ++i) { EXPECT_TRUE(value != std::nullopt) << "Didn't expect null value!"; - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); } } diff --git a/domain_tests/arbitrary_domains_protobuf_test.cc b/domain_tests/arbitrary_domains_protobuf_test.cc index 08ae160b..8ec6150e 100644 --- a/domain_tests/arbitrary_domains_protobuf_test.cc +++ b/domain_tests/arbitrary_domains_protobuf_test.cc @@ -91,7 +91,7 @@ TEST(ProtocolBuffer, int iterations = 10'000; while (--iterations > 0 && values.size() < 2) { values.insert(optional_get()); - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); } EXPECT_GT(iterations, 0) << "Field: " << name << " -- " << testing::PrintToString(values); @@ -109,7 +109,7 @@ TEST(ProtocolBuffer, if (field.size() > 0) { elem0.insert(field[0]); } - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); } EXPECT_GT(iterations, 0) << "Field: " << name << " -- " << testing::PrintToString(sizes) @@ -146,7 +146,7 @@ TEST(ProtocolBuffer, Value val(domain, bitgen); for (int i = 0; i < 10'000; ++i) { - val.Mutate(domain, bitgen, /*only_shrink=*/false); + val.Mutate(domain, bitgen, {}, false); } // We verify that the object actually has things in it. This can technically @@ -158,7 +158,7 @@ TEST(ProtocolBuffer, for (int iteration = 0; val.user_value.ByteSizeLong() > 0 && iteration < 50'000; ++iteration) { const auto prev = val; - val.Mutate(domain, bitgen, /*only_shrink=*/true); + val.Mutate(domain, bitgen, {}, true); ASSERT_TRUE(TowardsZero(prev.user_value, val.user_value)) << prev << " -vs- " << val; } @@ -175,7 +175,7 @@ TEST(ProtocolBufferWithRequiredFields, OptionalFieldIsEventuallySet) { ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; for (int i = 0; i < 1000; ++i) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; if (val.user_value.has_i32()) break; } @@ -196,7 +196,7 @@ TEST(ProtocolBufferWithRequiredFields, OptionalFieldIsEventuallyUnset) { // at least 1/800. Hence, within 11000 iterations we'll fail to observe this // event with probability at most 10^(-6). for (int i = 0; i < 11000; ++i) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; if (!val.user_value.has_i32()) break; } @@ -214,7 +214,7 @@ TEST(ProtocolBufferWithRequiredFields, OptionalFieldInSubprotoIsEventuallySet) { ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; for (int i = 0; i < 1000; ++i) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; if (val.user_value.has_req_sub() && val.user_value.req_sub().has_subproto_i32()) @@ -239,7 +239,7 @@ TEST(ProtocolBufferWithRequiredFields, // req_sub.subproto_i32 is at least 1/800. Hence, within 11000 iterations // we'll fail to observe this event with probability at most 10^(-6). for (int i = 0; i < 11000; ++i) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; if (val.user_value.has_req_sub() && !val.user_value.req_sub().has_subproto_i32()) @@ -271,7 +271,7 @@ TEST(ProtocolBufferWithRequiredFields, ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; for (int i = 0; i < 1000; ++i) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; if (val.user_value.has_sub_req()) { ASSERT_TRUE(val.user_value.sub_req().IsInitialized()) << val.user_value; @@ -298,7 +298,7 @@ TEST(ProtocolBufferWithRequiredFields, MapFieldIsEventuallyPopulated) { bool found = false; for (int i = 0; i < 1000 && !found; ++i) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; for (const auto& pair : val.user_value.map_sub_req()) { found = true; @@ -324,7 +324,7 @@ TEST(ProtocolBufferWithRequiredFields, ShrinkingNeverRemovesRequiredFields) { ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; for (int i = 0; i < 1000; ++i) { - val.Mutate(domain, bitgen, /*only_shrink=*/false); + val.Mutate(domain, bitgen, {}, false); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; } @@ -335,7 +335,7 @@ TEST(ProtocolBufferWithRequiredFields, ShrinkingNeverRemovesRequiredFields) { }; while (!is_minimal(val.user_value)) { - val.Mutate(domain, bitgen, /*only_shrink=*/true); + val.Mutate(domain, bitgen, {}, true); ASSERT_TRUE(val.user_value.IsInitialized()) << val.user_value; } } @@ -375,7 +375,7 @@ TEST(ProtocolBuffer, CanUsePerFieldDomains) { while (i32_values.size() < i32_count || str_values.size() < str_count || e_values.size() < e_count || rep_b_values.size() < rep_p_count || subproto_i32_values.size() < subproto_i32_count) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); if (val.user_value.has_i32()) i32_values.insert(val.user_value.i32()); if (val.user_value.has_str()) str_values.insert(val.user_value.str()); if (val.user_value.has_e()) e_values.insert(val.user_value.e()); @@ -616,9 +616,9 @@ TEST(ProtocolBufferEnum, Arbitrary) { Set s; while (s.size() < internal::TestProtobuf_Enum_descriptor()->value_count()) { s.insert(val.user_value); - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); } - val.Mutate(domain, bitgen, true); + val.Mutate(domain, bitgen, {}, true); } TEST(ArbitraryProtocolBufferEnum, InitGeneratesSeeds) { diff --git a/domain_tests/arbitrary_domains_test.cc b/domain_tests/arbitrary_domains_test.cc index d87e95be..24287595 100644 --- a/domain_tests/arbitrary_domains_test.cc +++ b/domain_tests/arbitrary_domains_test.cc @@ -49,6 +49,7 @@ namespace fuzztest { namespace { using ::fuzztest::domain_implementor::DomainBase; +using ::fuzztest::domain_implementor::MutationMetadata; using ::fuzztest::internal::IRObject; using ::testing::Contains; using ::testing::Each; @@ -67,9 +68,9 @@ TEST(BoolTest, Arbitrary) { Value b(domain, bitgen); bool copy = b.user_value; - b.Mutate(domain, bitgen, false); + b.Mutate(domain, bitgen, {}, false); EXPECT_NE(b.user_value, copy); - b.Mutate(domain, bitgen, false); + b.Mutate(domain, bitgen, {}, false); EXPECT_EQ(b.user_value, copy); } @@ -186,7 +187,7 @@ TYPED_TEST(MonostateTypeTest, Arbitrary) { auto v = domain.Init(bitgen); // Mutate "works". That is, it returns. // We don't expect it to do anything else since the value can't be changed. - domain.Mutate(v, bitgen, false); + domain.Mutate(v, bitgen, {}, false); } struct BinaryTree { @@ -207,7 +208,7 @@ TEST(UserDefinedAggregate, NestedArbitrary) { Set s; while (s.size() < 10) { s.insert(v.user_value.count_nodes()); - v.Mutate(domain, bitgen, false); + v.Mutate(domain, bitgen, {}, false); } } @@ -223,7 +224,8 @@ struct StatefulIncrementDomain return result; } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const MutationMetadata& metadata, bool only_shrink) { std::get<0>(val) += absl::Uniform(prng, 5, 6) + static_cast(only_shrink); } @@ -279,9 +281,9 @@ TEST(Domain, BasicVerify) { Value i(domain, bitgen); Value j = i; - i.Mutate(domain, bitgen, false); + i.Mutate(domain, bitgen, {}, false); EXPECT_THAT(i.user_value, j.user_value + 5); - i.Mutate(domain, bitgen, true); + i.Mutate(domain, bitgen, {}, true); EXPECT_THAT(i.user_value, j.user_value + 11); } diff --git a/domain_tests/container_combinators_test.cc b/domain_tests/container_combinators_test.cc index 92268872..53a9324c 100644 --- a/domain_tests/container_combinators_test.cc +++ b/domain_tests/container_combinators_test.cc @@ -93,7 +93,7 @@ TEST(ContainerCombinatorTest, MinSizeCanBeLargerThanDefaultMaxSize) { .WithMinSize(kDefaultMaxSize + 1); absl::BitGen bitgen; auto val = Value(domain, bitgen); - val.Mutate(domain, bitgen, /*only_shrink=*/false); + val.Mutate(domain, bitgen, {}, false); ASSERT_THAT(val.user_value, SizeIs(Gt(kDefaultMaxSize))); } diff --git a/domain_tests/container_test.cc b/domain_tests/container_test.cc index 1a3c1d91..0f361fc2 100644 --- a/domain_tests/container_test.cc +++ b/domain_tests/container_test.cc @@ -34,7 +34,7 @@ #include "absl/random/random.h" #include "./fuzztest/domain_core.h" #include "./domain_tests/domain_testing.h" -#include "./fuzztest/internal/coverage.h" +#include "./fuzztest/internal/table_of_recent_compares.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest { @@ -109,7 +109,7 @@ void TestMinMaxContainerSize(Domain domain, size_t min_size, size_t max_size) { ASSERT_THAT(v.user_value, size_match); sizes.insert(v.user_value.size()); - v.Mutate(domain, bitgen, false); + v.Mutate(domain, bitgen, {}, false); ASSERT_THAT(v.user_value, size_match); // Mutating the value can reach the max, but only for small max sizes @@ -117,7 +117,7 @@ void TestMinMaxContainerSize(Domain domain, size_t min_size, size_t max_size) { if (max_size <= 10) { auto max_v = v; while (max_v.user_value.size() < max_size) { - v.Mutate(domain, bitgen, false); + v.Mutate(domain, bitgen, {}, false); if (v.user_value.size() > max_v.user_value.size()) { max_v = v; } else if (v.user_value.size() < max_v.user_value.size()) { @@ -129,10 +129,10 @@ void TestMinMaxContainerSize(Domain domain, size_t min_size, size_t max_size) { // Shinking the value will reach the min. while (v.user_value.size() > min_size) { - v.Mutate(domain, bitgen, true); + v.Mutate(domain, bitgen, {}, true); } // Mutating again won't go below. - v.Mutate(domain, bitgen, true); + v.Mutate(domain, bitgen, {}, true); ASSERT_THAT(v.user_value, SizeIs(min_size)); } // Check that there is some in between. @@ -242,28 +242,16 @@ TEST(Container, ValidationRejectsInvalidElements) { // enabled, but we test on strings for simplification. TEST(Container, MemoryDictionaryMutationMutatesEveryPossibleMatch) { auto domain = Arbitrary(); - class ScopedExecutionCoverage { - public: - ScopedExecutionCoverage() { internal::SetExecutionCoverage(&coverage); } - - ~ScopedExecutionCoverage() { internal::SetExecutionCoverage(nullptr); } - - private: - internal::ExecutionCoverage coverage = - internal::ExecutionCoverage(/*counter_map=*/{}); - }; - ScopedExecutionCoverage scoped_coverage; - fuzztest::internal::GetExecutionCoverage() - ->GetTablesOfRecentCompares() - .GetMutable<0>() - .Insert(reinterpret_cast("abcd"), - reinterpret_cast("1234"), 4); + internal::TablesOfRecentCompares cmp_tables; + cmp_tables.GetMutable<0>().Insert(reinterpret_cast("abcd"), + reinterpret_cast("1234"), + 4); absl::BitGen bitgen; std::vector mutants; for (int i = 0; i < 1000000; ++i) { std::string mutant = "abcdabcdabcdabcd"; - domain.Mutate(mutant, bitgen, false); + domain.Mutate(mutant, bitgen, {.cmp_tables = &cmp_tables}, false); mutants.push_back(std::move(mutant)); } diff --git a/domain_tests/domain_testing.h b/domain_tests/domain_testing.h index eb156154..e8b9009a 100644 --- a/domain_tests/domain_testing.h +++ b/domain_tests/domain_testing.h @@ -35,6 +35,7 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "./fuzztest/internal/domains/mutation_metadata.h" #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" @@ -150,15 +151,20 @@ struct Value { }()), user_value(std::move(user_value)) {} - void Mutate(Domain& domain, absl::BitGenRef prng, bool only_shrink) { - domain.Mutate(corpus_value, prng, only_shrink); + void Mutate(Domain& domain, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + domain.Mutate(corpus_value, prng, metadata, only_shrink); user_value = domain.GetValue(corpus_value); } - void RandomizeByRepeatedMutation(Domain& domain, absl::BitGenRef prng) { + void RandomizeByRepeatedMutation( + Domain& domain, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata = {}, + bool only_shrink = false) { static constexpr int kMutations = 1000; for (int i = 0; i < kMutations; ++i) { - domain.Mutate(corpus_value, prng, /*only_shrink=*/false); + domain.Mutate(corpus_value, prng, metadata, only_shrink); } user_value = domain.GetValue(corpus_value); } @@ -255,8 +261,9 @@ template Value(Domain&, absl::BitGenRef) -> Value; template -auto GenerateValues(Domain domain, int num_seeds = 10, - int num_mutations = 100) { +auto GenerateValues(Domain domain, int num_seeds = 10, int num_mutations = 100, + const domain_implementor::MutationMetadata& metadata = {}, + bool only_shrink = false) { absl::BitGen bitgen; absl::flat_hash_set> seeds; @@ -275,7 +282,7 @@ auto GenerateValues(Domain domain, int num_seeds = 10, // As above, we repeat until we find enough unique ones. while (mutations.size() < num_mutations) { const auto previous = value; - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, metadata, only_shrink); // Make sure that it changed in some way. mutations.insert(value); EXPECT_NE(previous, value) << "Value=" << value << " Prev=" << previous; @@ -287,8 +294,10 @@ auto GenerateValues(Domain domain, int num_seeds = 10, } template -auto GenerateNonUniqueValues(Domain domain, int num_seeds = 10, - int num_mutations = 100) { +auto GenerateNonUniqueValues( + Domain domain, int num_seeds = 10, int num_mutations = 100, + const domain_implementor::MutationMetadata& metadata = {}, + bool only_shrink = false) { absl::BitGen bitgen; std::vector> seeds; @@ -302,7 +311,7 @@ auto GenerateNonUniqueValues(Domain domain, int num_seeds = 10, auto value = seed; std::vector> mutations = {value}; while (mutations.size() < num_mutations) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, metadata, only_shrink); mutations.push_back(value); } values.insert(values.end(), mutations.begin(), mutations.end()); @@ -330,28 +339,32 @@ void CheckValues(const Values& values, Pred pred) { } template -auto MutateUntilFoundN(Domain domain, size_t n) { +auto MutateUntilFoundN( + Domain domain, size_t n, + const domain_implementor::MutationMetadata& metadata = {}, + bool only_shrink = false) { absl::flat_hash_set> seen; absl::BitGen bitgen; Value val(domain, bitgen); while (seen.size() < n) { seen.insert(Value(val, domain)); - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, metadata, only_shrink); } return seen; } template > -absl::Status TestShrink(Domain domain, - const absl::flat_hash_set>& values, - IsTerminal is_terminal, IsCloser is_closer_to_zero) { +absl::Status TestShrink( + Domain domain, const absl::flat_hash_set>& values, + IsTerminal is_terminal, IsCloser is_closer_to_zero, + const domain_implementor::MutationMetadata& metadata = {}) { absl::BitGen bitgen; for (auto value : values) { while (!is_terminal(value.user_value)) { auto previous_value = value; - value.Mutate(domain, bitgen, true); + value.Mutate(domain, bitgen, metadata, true); if (value == previous_value) { return absl::InternalError( absl::StrCat("Mutate failed to produce a new value starting from ", @@ -377,7 +390,7 @@ void TestShrink(Domain domain, const Values& values, Pred pred) { for (const auto& value : values) { auto other_value = value; - other_value.Mutate(domain, bitgen, true); + other_value.Mutate(domain, bitgen, {}, true); ASSERT_TRUE(pred(value.user_value, other_value.user_value)) << value << " " << other_value; } diff --git a/domain_tests/in_grammar_domain_test.cc b/domain_tests/in_grammar_domain_test.cc index 196ab3ed..7b77aa73 100644 --- a/domain_tests/in_grammar_domain_test.cc +++ b/domain_tests/in_grammar_domain_test.cc @@ -69,7 +69,7 @@ TEST(InJsonGrammar, MutateGeneratesValidValues) { Value ast(domain, bitgen); while (valid_jsons.size() < 5000) { - ast.Mutate(domain, bitgen, false); + ast.Mutate(domain, bitgen, {}, false); nlohmann::json parsed_json = nlohmann::json::parse( ast.user_value.begin(), ast.user_value.end(), nullptr, false); EXPECT_FALSE(parsed_json.is_discarded()); @@ -88,7 +88,7 @@ TEST(InJsonGrammar, MutateGeneratesDifferentValuesWithHighProb) { while (valid_jsons.size() < 5000) { ++num_total_mutation; - ast.Mutate(domain, bitgen, false); + ast.Mutate(domain, bitgen, {}, false); nlohmann::json parsed_json = nlohmann::json::parse( ast.user_value.begin(), ast.user_value.end(), nullptr, false); valid_jsons.insert(parsed_json.dump()); @@ -116,7 +116,7 @@ TEST(InJsonGrammar, ShrinkModeReducesInputSize) { if (original_size > 20) ++expected_num_of_successful_shrink; for (int j = 0; j < 10; ++j) { - val.Mutate(domain, bitgen, true); + val.Mutate(domain, bitgen, {}, true); EXPECT_TRUE(val.corpus_value.NodeCount() <= original_size); } if (original_size > val.corpus_value.NodeCount()) { diff --git a/domain_tests/in_regexp_domain_test.cc b/domain_tests/in_regexp_domain_test.cc index 7e1d7dce..c7fe54dc 100644 --- a/domain_tests/in_regexp_domain_test.cc +++ b/domain_tests/in_regexp_domain_test.cc @@ -99,7 +99,7 @@ TEST(InRegexp, MutationGeneratesDifferentValidValues) { EXPECT_TRUE(RE2::FullMatch(val.user_value, regexp)); while (unique_values.size() < num_unique_values) { - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); EXPECT_TRUE(RE2::FullMatch(val.user_value, regexp)); unique_values.insert(val.user_value); } @@ -119,7 +119,7 @@ TEST(InRegexp, MutatingRepetitionCanIncreaseAndDecreaseLength) { constexpr int mutate_times = 50000; for (int i = 0; i < mutate_times; ++i) { val.corpus_value = domain.FromValue("aaaaa").value(); - val.Mutate(domain, bitgen, false); + val.Mutate(domain, bitgen, {}, false); size_t size_after_mutation = val.user_value.size(); ASSERT_LT(size_after_mutation, 7) << val.user_value; ++size_histogram[size_after_mutation]; @@ -161,7 +161,7 @@ TEST(InRegexp, OnlyShrinkFindsDifferentValueWithMinimalLength) { Value val(domain, bitgen); while (val.user_value.size() != expected_length) { size_t pre_size = val.user_value.size(); - val.Mutate(domain, bitgen, /*only_shrink=*/true); + val.Mutate(domain, bitgen, {}, true); if (min_path_length_equal_min_string_length) { ASSERT_LE(val.user_value.size(), pre_size) << val.user_value; } diff --git a/domain_tests/map_filter_combinator_test.cc b/domain_tests/map_filter_combinator_test.cc index cc805395..0c04638a 100644 --- a/domain_tests/map_filter_combinator_test.cc +++ b/domain_tests/map_filter_combinator_test.cc @@ -261,7 +261,7 @@ TEST(FlatMap, MutationAcceptsChangingDomains) { // We demand that our output domain has size `len` above. This will check // fail in ContainerOfImpl if we try to generate a string of the wrong // length. - domain.Mutate(mutated, bitgen, false); + domain.Mutate(mutated, bitgen, {}, false); } EXPECT_EQ(domain.GetValue(mutated).size(), std::get<1>(mutated)); } @@ -277,7 +277,7 @@ TEST(FlatMap, MutationAcceptsShrinkingOutputDomains) { } auto mutated = value->corpus_value; while (!domain.GetValue(mutated).empty()) { - domain.Mutate(mutated, bitgen, true); + domain.Mutate(mutated, bitgen, {}, true); } EXPECT_THAT(domain.GetValue(mutated), IsEmpty()); } @@ -298,7 +298,7 @@ TEST(FlatMap, MutationDoesNotAlterInputDomains) { auto mutated = value->corpus_value; const size_t original_size = value->user_value.size(); while (!all_zeros(domain.GetValue(mutated))) { - domain.Mutate(mutated, bitgen, true); + domain.Mutate(mutated, bitgen, {}, true); EXPECT_THAT(domain.GetValue(mutated).size(), Eq(original_size)); } EXPECT_THAT(domain.GetValue(mutated), Each(Eq(0))); @@ -341,7 +341,7 @@ TEST(Filter, CanFilterMutateCalls) { Value value(domain, bitgen); Set seen; while (seen.size() < 5) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); seen.insert(value.user_value); } EXPECT_THAT(seen, UnorderedElementsAre(2, 4, 6, 8, 10)); diff --git a/domain_tests/misc_domains_test.cc b/domain_tests/misc_domains_test.cc index a3a6d53b..9cc27ee1 100644 --- a/domain_tests/misc_domains_test.cc +++ b/domain_tests/misc_domains_test.cc @@ -61,7 +61,7 @@ TEST(BitFlagCombinationOf, Ints) { int val = 1 | 4 | 16 | 32; while (val != 0) { int prev = val; - domain.Mutate(val, bitgen, true); + domain.Mutate(val, bitgen, {}, true); // val can't have new bits set. EXPECT_EQ(prev & val, val); EXPECT_EQ(prev | val, prev); @@ -133,7 +133,7 @@ TEST(OneOf, AllSubDomainsArePickedEventually) { elems.clear(); Value mutated(domain, bitgen); while (elems.size() < 5) { - mutated.Mutate(domain, bitgen, false); + mutated.Mutate(domain, bitgen, {}, false); elems.insert(mutated.user_value); VerifyRoundTripThroughConversion(mutated, domain); @@ -155,13 +155,13 @@ TEST(OneOf, Mutate) { } else { v.corpus_value = std::variant(std::in_place_index_t<1>{}, k); } - v.Mutate(domain, bitgen, /*only_shrink=*/false); + v.Mutate(domain, bitgen, {}, false); ASSERT_NE(k, v.user_value); } for (int i = 0; i < kRuns; ++i) { Value v(domain, bitgen); int old_k = v.user_value; - v.Mutate(domain, bitgen, /*only_shrink=*/true); + v.Mutate(domain, bitgen, {}, true); int new_k = v.user_value; if ((new_k >= 0) == (old_k >= 0)) { EXPECT_LE(std::abs(new_k), std::abs(old_k)) @@ -181,7 +181,7 @@ TEST(OneOf, SwitchesDomains) { absl::flat_hash_set found; Value v(domain, bitgen); while (found.size() < all_colors.size()) { - v.Mutate(domain, bitgen, /*only_shrink=*/false); + v.Mutate(domain, bitgen, {}, false); found.insert(v.user_value); } ASSERT_THAT(found, UnorderedElementsAreArray(all_colors)); diff --git a/domain_tests/numeric_domains_test.cc b/domain_tests/numeric_domains_test.cc index 492e9f03..e8cf2326 100644 --- a/domain_tests/numeric_domains_test.cc +++ b/domain_tests/numeric_domains_test.cc @@ -352,7 +352,7 @@ TEST(InRange, SupportsSingletonRange) { auto domain = InRange(10, 10); absl::BitGen bitgen; auto val = Value(domain, bitgen); - val.Mutate(domain, bitgen, /*only_shrink=*/false); + val.Mutate(domain, bitgen, {}, false); EXPECT_EQ(val.user_value, 10); } @@ -379,11 +379,11 @@ TEST(IllegalInputs, Numeric) { const std::vector values{-10, -1, 0, 1}; auto restricted_domain = Positive(); for (int value : values) { - restricted_domain.Mutate(value, bitgen, false); + restricted_domain.Mutate(value, bitgen, {}, false); ASSERT_GT(value, 0); } for (int value : values) { - restricted_domain.Mutate(value, bitgen, true); + restricted_domain.Mutate(value, bitgen, {}, true); ASSERT_GT(value, 0); } } diff --git a/domain_tests/pointer_domains_test.cc b/domain_tests/pointer_domains_test.cc index fe1b5360..58ceedc0 100644 --- a/domain_tests/pointer_domains_test.cc +++ b/domain_tests/pointer_domains_test.cc @@ -41,7 +41,7 @@ void TestSmartPointer(Domain domain) { Set> mutations; while (mutations.size() < 4) { - value.Mutate(domain, bitgen, false); + value.Mutate(domain, bitgen, {}, false); mutations.insert(value.user_value ? std::optional(*value.user_value) : std::nullopt); } diff --git a/domain_tests/recursive_domains_test.cc b/domain_tests/recursive_domains_test.cc index e9072884..29dfd58c 100644 --- a/domain_tests/recursive_domains_test.cc +++ b/domain_tests/recursive_domains_test.cc @@ -57,7 +57,7 @@ TEST(DomainBuilder, DomainForRecursiveDataStructureCreatesUniqueObjects) { Value tree(domain, bitgen); while (true) { - tree.Mutate(domain, bitgen, false); + tree.Mutate(domain, bitgen, {}, false); if (tree.user_value.children.empty()) continue; if (tree.user_value.children[0].children.empty()) continue; if (tree.user_value.children[0].children[0].children.empty()) continue; @@ -98,7 +98,7 @@ TEST(DomainBuilder, absl::BitGen bitgen; Value redtree(domain_redtree, bitgen); while (true) { - redtree.Mutate(domain_redtree, bitgen, false); + redtree.Mutate(domain_redtree, bitgen, {}, false); if (redtree.user_value.children.empty()) continue; if (redtree.user_value.children[0].children.empty()) continue; if (redtree.user_value.children[0].children[0].children.empty()) continue; diff --git a/domain_tests/specific_value_domains_test.cc b/domain_tests/specific_value_domains_test.cc index 215a6f98..d65ca099 100644 --- a/domain_tests/specific_value_domains_test.cc +++ b/domain_tests/specific_value_domains_test.cc @@ -50,7 +50,7 @@ TEST(ElementOfTest, TwoOptions) { for (const auto& v : found) { auto copy = v; - copy.Mutate(domain, bitgen, false); + copy.Mutate(domain, bitgen, {}, false); EXPECT_NE(v, copy); } } @@ -87,7 +87,7 @@ TEST(ElementOfTest, Colors) { found.clear(); Value c(domain, bitgen); while (found.size() < all_colors.size()) { - c.Mutate(domain, bitgen, false); + c.Mutate(domain, bitgen, {}, false); found.insert(c.user_value); VerifyRoundTripThroughConversion(c, domain); @@ -97,7 +97,7 @@ TEST(ElementOfTest, Colors) { c = Value(domain, bitgen); while (c.user_value != Color::Red) { auto prev = c.user_value; - c.Mutate(domain, bitgen, true); + c.Mutate(domain, bitgen, {}, true); ASSERT_LE(c.user_value, prev); } ASSERT_THAT(found, UnorderedElementsAreArray(all_colors)); @@ -146,7 +146,7 @@ TEST(Just, Basic) { int n = 3; for (int i = 0; i < 10; ++i) { Value v(domain, bitgen); - v.Mutate(domain, bitgen, false); + v.Mutate(domain, bitgen, {}, false); VerifyRoundTripThroughConversion(v, domain); EXPECT_EQ(n, 3); diff --git a/domain_tests/string_domains_test.cc b/domain_tests/string_domains_test.cc index 057b0371..4b1b7f5b 100644 --- a/domain_tests/string_domains_test.cc +++ b/domain_tests/string_domains_test.cc @@ -96,7 +96,7 @@ TEST(Domain, Forwarding) { elems.clear(); Value c(domain, bitgen); while (elems.size() < 'z' - 'a' + 1) { - c.Mutate(domain, bitgen, false); + c.Mutate(domain, bitgen, {}, false); elems.insert(c.user_value); } } diff --git a/fuzztest/BUILD b/fuzztest/BUILD index 6829abef..a51c7c99 100644 --- a/fuzztest/BUILD +++ b/fuzztest/BUILD @@ -155,6 +155,7 @@ cc_library( testonly = True, srcs = ["llvm_fuzzer_wrapper.cc"], deps = [ + ":coverage", ":domain_core", ":fuzztest", ":fuzztest_macros", @@ -210,11 +211,11 @@ cc_library( ":any", ":configuration", ":corpus_database", - ":coverage", ":domain_core", ":fixture_driver", ":logging", ":runtime", + ":table_of_recent_compares", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/log", "@com_google_absl//absl/memory", @@ -303,6 +304,7 @@ cc_library( "//conditions:default": [], }), deps = [ + ":domain_core", ":flag_name", ":logging", ":table_of_recent_compares", @@ -366,6 +368,7 @@ cc_library( "internal/domains/flat_map_impl.h", "internal/domains/in_range_impl.h", "internal/domains/map_impl.h", + "internal/domains/mutation_metadata.h", "internal/domains/one_of_impl.h", "internal/domains/optional_of_impl.h", "internal/domains/overlap_of_impl.h", @@ -380,7 +383,6 @@ cc_library( deps = [ ":absl_helpers", ":any", - ":coverage", ":logging", ":meta", ":printer", diff --git a/fuzztest/CMakeLists.txt b/fuzztest/CMakeLists.txt index 834c254c..9af36ddb 100644 --- a/fuzztest/CMakeLists.txt +++ b/fuzztest/CMakeLists.txt @@ -119,6 +119,7 @@ fuzztest_cc_library( SRCS "llvm_fuzzer_wrapper.cc" DEPS + fuzztest::coverage fuzztest::domain_core fuzztest::fuzztest fuzztest::io @@ -241,6 +242,7 @@ fuzztest_cc_library( SRCS "internal/coverage.cc" DEPS + fuzztest::domain_core fuzztest::flag_name fuzztest::logging fuzztest::table_of_recent_compares @@ -299,6 +301,7 @@ fuzztest_cc_library( "internal/domains/flat_map_impl.h" "internal/domains/in_range_impl.h" "internal/domains/map_impl.h" + "internal/domains/mutation_metadata.h" "internal/domains/one_of_impl.h" "internal/domains/optional_of_impl.h" "internal/domains/overlap_of_impl.h" @@ -313,7 +316,6 @@ fuzztest_cc_library( DEPS fuzztest::absl_helpers fuzztest::any - fuzztest::coverage fuzztest::logging fuzztest::meta fuzztest::printer diff --git a/fuzztest/domain_core.h b/fuzztest/domain_core.h index eee4a928..1de526e2 100644 --- a/fuzztest/domain_core.h +++ b/fuzztest/domain_core.h @@ -162,12 +162,16 @@ class DomainBuilder { return GetInnerDomain().Init(prng); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { - GetInnerDomain().Mutate(val, prng, only_shrink); + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + GetInnerDomain().Mutate(val, prng, metadata, only_shrink); } - void UpdateMemoryDictionary(const corpus_type& val) { - return GetInnerDomain().UpdateMemoryDictionary(val); + void UpdateMemoryDictionary( + const corpus_type& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) { + return GetInnerDomain().UpdateMemoryDictionary(val, cmp_tables); } auto GetPrinter() const { return GetInnerDomain().GetPrinter(); } @@ -216,12 +220,16 @@ class DomainBuilder { 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 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) { - return inner_.UpdateMemoryDictionary(val); + void UpdateMemoryDictionary( + const corpus_type& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) { + return inner_.UpdateMemoryDictionary(val, cmp_tables); } auto GetPrinter() const { return inner_.GetPrinter(); } diff --git a/fuzztest/internal/centipede_adaptor.cc b/fuzztest/internal/centipede_adaptor.cc index 9e74688b..de9961e0 100644 --- a/fuzztest/internal/centipede_adaptor.cc +++ b/fuzztest/internal/centipede_adaptor.cc @@ -62,11 +62,11 @@ #include "./fuzztest/internal/any.h" #include "./fuzztest/internal/configuration.h" #include "./fuzztest/internal/corpus_database.h" -#include "./fuzztest/internal/coverage.h" #include "./fuzztest/internal/domains/domain.h" #include "./fuzztest/internal/fixture_driver.h" #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/runtime.h" +#include "./fuzztest/internal/table_of_recent_compares.h" namespace fuzztest::internal { namespace { @@ -162,12 +162,6 @@ class CentipedeAdaptorRunnerCallbacks : public centipede::RunnerCallbacks { fuzzer_impl_(*fuzzer_impl), configuration_(*configuration), prng_(GetRandomSeed()) { - if (GetExecutionCoverage() == nullptr) { - execution_coverage_ = std::make_unique( - /*counter_map=*/absl::Span{}); - execution_coverage_->SetIsTracing(true); - SetExecutionCoverage(execution_coverage_.get()); - } } bool Execute(centipede::ByteSpan input) override { @@ -254,7 +248,7 @@ class CentipedeAdaptorRunnerCallbacks : public centipede::RunnerCallbacks { parsed_origin = fuzzer_impl_.params_domain_.Init(prng_); } auto mutant = FuzzTestFuzzerImpl::Input{*std::move(parsed_origin)}; - fuzzer_impl_.MutateValue(mutant, prng_); + fuzzer_impl_.MutateValue(mutant, prng_, {.cmp_tables = &cmp_tables_}); mutant_data = fuzzer_impl_.params_domain_.SerializeCorpus(mutant.args).ToString(); } @@ -266,27 +260,22 @@ class CentipedeAdaptorRunnerCallbacks : public centipede::RunnerCallbacks { ~CentipedeAdaptorRunnerCallbacks() override { runtime_.UnsetCurrentArgs(); - if (GetExecutionCoverage() == execution_coverage_.get()) - SetExecutionCoverage(nullptr); } private: template - static void InsertCmpEntryIntoIntegerDictionary(const uint8_t* a, - const uint8_t* b) { + void InsertCmpEntryIntoIntegerDictionary(const uint8_t* a, const uint8_t* b) { T a_int; T b_int; memcpy(&a_int, a, sizeof(T)); memcpy(&b_int, b, sizeof(T)); - GetExecutionCoverage() - ->GetTablesOfRecentCompares() - .GetMutable() - .Insert(a_int, b_int); + cmp_tables_.GetMutable().Insert(a_int, b_int); } void SetMetadata(const centipede::ExecutionMetadata* metadata) { if (metadata == nullptr) return; - metadata->ForEachCmpEntry([](centipede::ByteSpan a, centipede::ByteSpan b) { + metadata->ForEachCmpEntry([this](centipede::ByteSpan a, + centipede::ByteSpan b) { FUZZTEST_INTERNAL_CHECK(a.size() == b.size(), "cmp operands must have the same size"); const size_t size = a.size(); @@ -299,10 +288,7 @@ class CentipedeAdaptorRunnerCallbacks : public centipede::RunnerCallbacks { } else if (size == 8) { InsertCmpEntryIntoIntegerDictionary(a.data(), b.data()); } - GetExecutionCoverage() - ->GetTablesOfRecentCompares() - .GetMutable<0>() - .Insert(a.data(), b.data(), size); + cmp_tables_.GetMutable<0>().Insert(a.data(), b.data(), size); }); } @@ -313,7 +299,7 @@ class CentipedeAdaptorRunnerCallbacks : public centipede::RunnerCallbacks { Runtime& runtime_; FuzzTestFuzzerImpl& fuzzer_impl_; const Configuration& configuration_; - std::unique_ptr execution_coverage_; + internal::TablesOfRecentCompares cmp_tables_; absl::BitGen prng_; }; @@ -409,7 +395,6 @@ class CentipedeAdaptorEngineCallbacks : public centipede::CentipedeCallbacks { CentipedeAdaptorRunnerCallbacks runner_callbacks_; size_t batch_result_buffer_size_; uint8_t* batch_result_buffer_; - std::unique_ptr execution_coverage_; }; class CentipedeAdaptorEngineCallbacksFactory diff --git a/fuzztest/internal/domains/aggregate_of_impl.h b/fuzztest/internal/domains/aggregate_of_impl.h index ef04061e..d51a5cea 100644 --- a/fuzztest/internal/domains/aggregate_of_impl.h +++ b/fuzztest/internal/domains/aggregate_of_impl.h @@ -53,6 +53,8 @@ class AggregateOfImpl using typename AggregateOfImpl::DomainBase::corpus_type; using typename AggregateOfImpl::DomainBase::value_type; + using AggregateOfImpl::DomainBase::Mutate; + AggregateOfImpl() = default; explicit AggregateOfImpl(std::in_place_t, Inner... inner) : inner_(std::move(inner)...) {} @@ -64,7 +66,9 @@ class AggregateOfImpl inner_); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { std::integral_constant size; auto bound = internal::BindAggregate(val, size); // Filter the tuple to only the mutable fields. @@ -79,12 +83,14 @@ class AggregateOfImpl int offset = absl::Uniform(prng, 0, actual_size); Switch(offset, [&](auto I) { std::get(inner_).Mutate(std::get(bound), - prng, only_shrink); + prng, metadata, only_shrink); }); } } - void UpdateMemoryDictionary(const corpus_type& val) { + void UpdateMemoryDictionary( + const corpus_type& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) { // Copy codes from Mutate that does the mutable domain filtering things. std::integral_constant size; auto bound = internal::BindAggregate(val, size); @@ -97,7 +103,7 @@ class AggregateOfImpl if constexpr (actual_size > 0) { ApplyIndex([&](auto... I) { (std::get(inner_).UpdateMemoryDictionary( - std::get(bound)), + std::get(bound), cmp_tables), ...); }); } diff --git a/fuzztest/internal/domains/arbitrary_impl.h b/fuzztest/internal/domains/arbitrary_impl.h index a709ec45..f0ca017f 100644 --- a/fuzztest/internal/domains/arbitrary_impl.h +++ b/fuzztest/internal/domains/arbitrary_impl.h @@ -33,7 +33,6 @@ #include "absl/random/distributions.h" #include "absl/strings/string_view.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" @@ -73,9 +72,12 @@ class ArbitraryImpl>> public: using typename ArbitraryImpl::DomainBase::value_type; + using ArbitraryImpl::DomainBase::Mutate; + value_type Init(absl::BitGenRef) { return value_type{}; } - void Mutate(value_type&, absl::BitGenRef, bool) {} + void Mutate(value_type&, absl::BitGenRef, + const domain_implementor::MutationMetadata&, bool) {} value_type GetRandomCorpusValue(absl::BitGenRef prng) { return value_type{}; } @@ -91,12 +93,15 @@ template <> class ArbitraryImpl : public domain_implementor::DomainBase> { public: + using ArbitraryImpl::DomainBase::Mutate; + value_type Init(absl::BitGenRef prng) { if (auto seed = MaybeGetRandomSeed(prng)) return *seed; return static_cast(absl::Uniform(prng, 0, 2)); } - void Mutate(value_type& val, absl::BitGenRef, bool only_shrink) { + void Mutate(value_type& val, absl::BitGenRef, + const domain_implementor::MutationMetadata&, bool only_shrink) { if (only_shrink) { val = false; } else { @@ -121,6 +126,8 @@ class ArbitraryImpl && public: using typename ArbitraryImpl::DomainBase::value_type; + using ArbitraryImpl::DomainBase::Mutate; + static constexpr bool is_memory_dictionary_compatible_v = sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8; using IntegerDictionaryT = @@ -137,7 +144,9 @@ class ArbitraryImpl && } } - void Mutate(value_type& val, absl::BitGenRef prng, bool only_shrink) { + 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; @@ -152,10 +161,10 @@ class ArbitraryImpl && if (absl::Bernoulli(prng, 0.25)) { RandomBitFlip(prng, val, sizeof(T) * 8); } else { - RandomWalkOrUniformOrDict<5>(prng, val, std::numeric_limits::min(), - std::numeric_limits::max(), - temporary_dict_, permanent_dict_, - permanent_dict_candidate_); + RandomWalkOrUniformOrDict<5>( + prng, val, std::numeric_limits::min(), + std::numeric_limits::max(), metadata.cmp_tables, temporary_dict_, + permanent_dict_, permanent_dict_candidate_); } // Make sure Mutate really mutates. } while (val == prev); @@ -166,12 +175,13 @@ class ArbitraryImpl && return ChooseFromAll(prng); } - void UpdateMemoryDictionary(const value_type& val) { + void UpdateMemoryDictionary( + const value_type& val, domain_implementor::ConstCmpTablesPtr cmp_tables) { if constexpr (is_memory_dictionary_compatible_v) { - if (GetExecutionCoverage() != nullptr) { + if (cmp_tables != nullptr) { temporary_dict_.MatchEntriesFromTableOfRecentCompares( - val, GetExecutionCoverage()->GetTablesOfRecentCompares(), - std::numeric_limits::min(), std::numeric_limits::max()); + val, *cmp_tables, std::numeric_limits::min(), + std::numeric_limits::max()); if (permanent_dict_candidate_.has_value() && permanent_dict_.Size() < kPermanentDictMaxSize) { permanent_dict_.AddEntry(std::move(*permanent_dict_candidate_)); @@ -216,14 +226,18 @@ class ArbitraryImpl using typename ArbitraryImpl::DomainBase::corpus_type; using typename ArbitraryImpl::DomainBase::value_type; + using ArbitraryImpl::DomainBase::Mutate; + 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, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { unsigned char u8 = std::to_integer(val); - inner_.Mutate(u8, prng, only_shrink); + inner_.Mutate(u8, prng, metadata, only_shrink); val = std::byte{u8}; } @@ -246,13 +260,16 @@ class ArbitraryImpl>> public: using typename ArbitraryImpl::DomainBase::value_type; + using ArbitraryImpl::DomainBase::Mutate; + value_type Init(absl::BitGenRef prng) { if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed; return ChooseOneOr(SpecialValues::Get(), prng, [&] { return absl::Uniform(prng, T{0}, T{1}); }); } - void Mutate(value_type& val, absl::BitGenRef prng, bool only_shrink) { + 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}); @@ -318,17 +335,23 @@ class ArbitraryImpl> using typename ArbitraryImpl::DomainBase::corpus_type; using typename ArbitraryImpl::DomainBase::value_type; + using ArbitraryImpl::DomainBase::Mutate; + 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, bool only_shrink) { - inner_.Mutate(val, prng, only_shrink); + 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) { - inner_.UpdateMemoryDictionary(val); + void UpdateMemoryDictionary( + const corpus_type& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) { + inner_.UpdateMemoryDictionary(val, cmp_tables); } auto GetPrinter() const { return StringPrinter{}; } @@ -373,17 +396,23 @@ class ArbitraryImpl using typename ArbitraryImpl::DomainBase::corpus_type; using typename ArbitraryImpl::DomainBase::value_type; + using ArbitraryImpl::DomainBase::Mutate; + 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, bool only_shrink) { - inner_.Mutate(val, prng, only_shrink); + 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) { - inner_.UpdateMemoryDictionary(val); + void UpdateMemoryDictionary( + const corpus_type& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) { + inner_.UpdateMemoryDictionary(val, cmp_tables); } auto GetPrinter() const { return StringPrinter{}; } diff --git a/fuzztest/internal/domains/bit_flag_combination_of_impl.h b/fuzztest/internal/domains/bit_flag_combination_of_impl.h index 2a56d023..ce7642e6 100644 --- a/fuzztest/internal/domains/bit_flag_combination_of_impl.h +++ b/fuzztest/internal/domains/bit_flag_combination_of_impl.h @@ -33,11 +33,14 @@ class BitFlagCombinationOfImpl public: using typename BitFlagCombinationOfImpl::DomainBase::value_type; + using BitFlagCombinationOfImpl::DomainBase::Mutate; + explicit BitFlagCombinationOfImpl(absl::Span flags) : flags_(flags.begin(), flags.end()), all_flags_combo_{} { FUZZTEST_INTERNAL_CHECK_PRECONDITION( !flags.empty(), "BitFlagCombinationOf requires a non empty list."); - // Make sure they are mutually exclusive options, and none are empty. + // Make sure they are mutually exclusive metadata, only_shrink and none are + // empty. for (int i = 0; i < flags.size(); ++i) { T v1 = flags[i]; FUZZTEST_INTERNAL_CHECK_PRECONDITION( @@ -57,7 +60,8 @@ class BitFlagCombinationOfImpl return value_type{}; } - void Mutate(value_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(value_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata&, bool only_shrink) { T to_switch = flags_[ChooseOffset(flags_.size(), prng)]; if (!only_shrink || BitAnd(val, to_switch) != T{}) { diff --git a/fuzztest/internal/domains/container_mutation_helpers.h b/fuzztest/internal/domains/container_mutation_helpers.h index c6c17de7..f17d52fa 100644 --- a/fuzztest/internal/domains/container_mutation_helpers.h +++ b/fuzztest/internal/domains/container_mutation_helpers.h @@ -22,7 +22,6 @@ #include "absl/random/bit_gen_ref.h" #include "absl/random/distributions.h" -#include "./fuzztest/internal/coverage.h" #include "./fuzztest/internal/table_of_recent_compares.h" namespace fuzztest::internal { @@ -205,6 +204,7 @@ bool ApplyDictionaryMutationAndSavePermanentCandidate( template bool MemoryDictionaryMutation( ContainerT& val, absl::BitGenRef prng, + const internal::TablesOfRecentCompares* cmp_tables, ContainerDictionary& temporary_dict, ContainerDictionary& manual_dict, ContainerDictionary& permanent_dict, @@ -214,9 +214,9 @@ bool MemoryDictionaryMutation( const bool can_use_manual_dictionary = !manual_dict.IsEmpty(); const bool can_use_temporary_dictionary = !temporary_dict.IsEmpty(); const bool can_use_permanent_dictionary = !permanent_dict.IsEmpty(); - const int dictionary_action_count = 1 + can_use_manual_dictionary + - can_use_temporary_dictionary + - can_use_permanent_dictionary; + const int dictionary_action_count = + can_use_manual_dictionary + can_use_temporary_dictionary + + can_use_permanent_dictionary + (cmp_tables == nullptr ? 0 : 1); int dictionary_action = absl::Uniform(prng, 0, dictionary_action_count); if (can_use_temporary_dictionary && dictionary_action-- == 0) { mutated = ApplyDictionaryMutationAndSavePermanentCandidate( @@ -245,7 +245,7 @@ bool MemoryDictionaryMutation( // Pick entries from tables_of_recent_compares(TORC) directly. if (dictionary_action-- == 0) { auto dictionary_entry = ContainerDictionary::GetRandomTORCEntry( - val, prng, GetExecutionCoverage()->GetTablesOfRecentCompares()); + val, prng, *cmp_tables); if (dictionary_entry.has_value()) { mutated = ApplyDictionaryMutationAndSavePermanentCandidate( val, *dictionary_entry, prng, permanent_dict_candidate, max_size); diff --git a/fuzztest/internal/domains/container_of_impl.h b/fuzztest/internal/domains/container_of_impl.h index 9fe7372b..70ee9383 100644 --- a/fuzztest/internal/domains/container_of_impl.h +++ b/fuzztest/internal/domains/container_of_impl.h @@ -31,14 +31,12 @@ #include "absl/status/status.h" #include "absl/strings/str_format.h" #include "absl/types/span.h" -#include "./fuzztest/internal/coverage.h" #include "./fuzztest/internal/domains/container_mutation_helpers.h" #include "./fuzztest/internal/domains/domain_base.h" #include "./fuzztest/internal/logging.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 { @@ -87,6 +85,8 @@ class ContainerOfImplBase : public domain_implementor::DomainBase< using typename ContainerOfImplBase::DomainBase::corpus_type; using typename ContainerOfImplBase::DomainBase::value_type; + using ContainerOfImplBase::DomainBase::Mutate; + // Some container mutation only applies to vector or string types which do // not have a custom corpus type. static constexpr bool is_vector_or_string = @@ -112,7 +112,9 @@ class ContainerOfImplBase : public domain_implementor::DomainBase< ContainerOfImplBase() = default; explicit ContainerOfImplBase(InnerDomainT inner) : inner_(std::move(inner)) {} - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { permanent_dict_candidate_ = std::nullopt; FUZZTEST_INTERNAL_CHECK( min_size() <= val.size() && val.size() <= max_size(), "Size ", @@ -123,7 +125,7 @@ class ContainerOfImplBase : public domain_implementor::DomainBase< const bool can_change = val.size() != 0; const bool can_use_memory_dict = !only_shrink && container_has_memory_dict && can_change && - GetExecutionCoverage() != nullptr; + metadata.cmp_tables != nullptr; const int action_count = can_shrink + can_grow + can_change + can_use_memory_dict; @@ -162,12 +164,12 @@ class ContainerOfImplBase : public domain_implementor::DomainBase< auto it_start = std::next(val.begin(), change_offset); auto it_end = std::next(it_start, changes); for (; it_start != it_end; it_start = std::next(it_start)) { - Self().MutateElement(val, prng, it_start, only_shrink); + Self().MutateElement(val, prng, metadata, only_shrink, it_start); } return; } - Self().MutateElement( - val, prng, ChoosePosition(val, IncludeEnd::kNo, prng), only_shrink); + Self().MutateElement(val, prng, metadata, only_shrink, + ChoosePosition(val, IncludeEnd::kNo, prng)); return; } } @@ -175,13 +177,12 @@ class ContainerOfImplBase : public domain_implementor::DomainBase< if (can_use_memory_dict) { if (action-- == 0) { bool mutated = MemoryDictionaryMutation( - val, prng, temporary_dict_, GetManualDict(), permanent_dict_, - permanent_dict_candidate_, max_size()); + val, prng, metadata.cmp_tables, temporary_dict_, GetManualDict(), + permanent_dict_, permanent_dict_candidate_, max_size()); // If dict failed, fall back to changing an element. if (!mutated) { - Self().MutateElement(val, prng, - ChoosePosition(val, IncludeEnd::kNo, prng), - only_shrink); + Self().MutateElement(val, prng, metadata, only_shrink, + ChoosePosition(val, IncludeEnd::kNo, prng)); } return; } @@ -189,16 +190,17 @@ class ContainerOfImplBase : public domain_implementor::DomainBase< } } - void UpdateMemoryDictionary(const corpus_type& val) { + void UpdateMemoryDictionary( + const corpus_type& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) { // TODO(JunyangShao): Implement dictionary propagation to container // elements. For now the propagation stops in container domains. // Because all elements share an `inner_` and will share // a dictionary if we propagate it, which makes the dictionary // not efficient. if constexpr (container_has_memory_dict) { - if (GetExecutionCoverage() != nullptr) { - temporary_dict_.MatchEntriesFromTableOfRecentCompares( - val, GetExecutionCoverage()->GetTablesOfRecentCompares()); + if (cmp_tables != nullptr) { + temporary_dict_.MatchEntriesFromTableOfRecentCompares(val, *cmp_tables); if (permanent_dict_candidate_.has_value() && permanent_dict_.Size() < kPermanentDictMaxSize) { permanent_dict_.AddEntry(std::move(*permanent_dict_candidate_)); @@ -494,7 +496,8 @@ Please verify that the inner domain can provide enough values. // Try to mutate the element in `it`. void MutateElement(corpus_type& val, absl::BitGenRef prng, - typename corpus_type::iterator it, bool only_shrink) { + const domain_implementor::MutationMetadata& metadata, + bool only_shrink, typename corpus_type::iterator it) { size_t failures_allowed = 100; // Try a few times to mutate the element. // If the mutation reduces the number of elements in the container it means @@ -510,7 +513,7 @@ Please verify that the inner domain can provide enough values. while (failures_allowed > 0) { auto new_element = original_element_list.front(); - this->inner_.Mutate(new_element, prng, only_shrink); + this->inner_.Mutate(new_element, prng, metadata, only_shrink); if (real_value.insert(this->inner_.GetValue(new_element)).second) { val.push_back(std::move(new_element)); return; @@ -553,13 +556,14 @@ class SequenceContainerOfImpl return total_weight; } - uint64_t MutateSelectedField(corpus_type& val, absl::BitGenRef prng, - bool only_shrink, - uint64_t selected_field_index) { + uint64_t MutateSelectedField( + corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, bool only_shrink, + uint64_t selected_field_index) { uint64_t field_counter = 0; for (auto& i : val) { field_counter += this->inner_.MutateSelectedField( - i, prng, only_shrink, selected_field_index - field_counter); + i, prng, metadata, only_shrink, selected_field_index - field_counter); if (field_counter >= selected_field_index) break; } return field_counter; @@ -574,8 +578,9 @@ class SequenceContainerOfImpl } void MutateElement(corpus_type&, absl::BitGenRef prng, - typename corpus_type::iterator it, bool only_shrink) { - this->inner_.Mutate(*it, prng, only_shrink); + const domain_implementor::MutationMetadata& metadata, + bool only_shrink, typename corpus_type::iterator it) { + this->inner_.Mutate(*it, prng, metadata, only_shrink); } }; diff --git a/fuzztest/internal/domains/domain.h b/fuzztest/internal/domains/domain.h index 8d0ad2fa..6faa9fc8 100644 --- a/fuzztest/internal/domains/domain.h +++ b/fuzztest/internal/domains/domain.h @@ -31,6 +31,7 @@ #include "./fuzztest/internal/domains/domain_type_erasure.h" // IWYU pragma: export #include "./fuzztest/internal/printer.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/table_of_recent_compares.h" namespace fuzztest { namespace internal { @@ -156,13 +157,23 @@ class Domain { // Mutate() makes a relatively small modification on `val` of `corpus_type`. // - // Used during coverage-guided fuzzing. When `only_shrink` is enabled, the - // mutated value is always "simpler" (e.g., smaller). This is used for input - // minimization ("shrinking"). + // Used during coverage-guided fuzzing. When `only_shrink` is true, + // the mutated value is always "simpler" (e.g., smaller). This is used for + // input minimization ("shrinking"). // // ENSURES: That the mutated value is not the same as the original. - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { - return inner_->UntypedMutate(val, prng, only_shrink); + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + return inner_->UntypedMutate(val, prng, metadata, only_shrink); + } + + // Mutates `corpus_value` using `prng`, `only_shirnk` and the default mutation + // metadata. This is a temporary wrapper that redirects the call to the real + // interface with an explicit argument for metadata. + void Mutate(corpus_type& corpus_value, absl::BitGenRef prng, + bool only_shrink) { + return Mutate(corpus_value, prng, {}, only_shrink); } // The methods below are responsible for transforming between the above @@ -232,8 +243,10 @@ class Domain { // // TODO(b/303324603): Using an extension mechanism, expose this method in // the interface only for user value types `T` for which it makes sense. - void UpdateMemoryDictionary(const corpus_type& corpus_value) { - return inner_->UntypedUpdateMemoryDictionary(corpus_value); + void UpdateMemoryDictionary( + const corpus_type& corpus_value, + const internal::TablesOfRecentCompares* cmp_tables) { + return inner_->UntypedUpdateMemoryDictionary(corpus_value, cmp_tables); } // Return the field counts of `corpus_value` if `corpus_value` is @@ -251,11 +264,12 @@ class Domain { // // TODO(b/303324603): Using an extension mechanism, expose this method in // the interface only for user value types `T` for which it makes sense. - uint64_t MutateSelectedField(corpus_type& corpus_value, absl::BitGenRef prng, - bool only_shrink, - uint64_t selected_field_index) { - return inner_->UntypedMutateSelectedField(corpus_value, prng, only_shrink, - selected_field_index); + uint64_t MutateSelectedField( + corpus_type& corpus_value, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, bool only_shrink, + uint64_t selected_field_index) { + return inner_->UntypedMutateSelectedField( + corpus_value, prng, metadata, only_shrink, selected_field_index); } private: @@ -302,9 +316,15 @@ class UntypedDomain { corpus_type Init(absl::BitGenRef prng) { return inner_->UntypedInit(prng); } + void Mutate(corpus_type& corpus_value, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + return inner_->UntypedMutate(corpus_value, prng, metadata, only_shrink); + } + void Mutate(corpus_type& corpus_value, absl::BitGenRef prng, bool only_shrink) { - return inner_->UntypedMutate(corpus_value, prng, only_shrink); + return Mutate(corpus_value, prng, {}, only_shrink); } value_type GetValue(const corpus_type& corpus_value) const { @@ -325,8 +345,10 @@ class UntypedDomain { auto GetPrinter() const { return internal::GenericPrinter{*inner_}; } - void UpdateMemoryDictionary(const corpus_type& corpus_value) { - return inner_->UntypedUpdateMemoryDictionary(corpus_value); + void UpdateMemoryDictionary( + const corpus_type& corpus_value, + domain_implementor::ConstCmpTablesPtr cmp_tables) { + return inner_->UntypedUpdateMemoryDictionary(corpus_value, cmp_tables); } private: diff --git a/fuzztest/internal/domains/domain_base.h b/fuzztest/internal/domains/domain_base.h index 393bfe85..743a3abd 100644 --- a/fuzztest/internal/domains/domain_base.h +++ b/fuzztest/internal/domains/domain_base.h @@ -31,10 +31,10 @@ #include "absl/status/status.h" #include "absl/strings/str_format.h" #include "./fuzztest/internal/domains/domain_type_erasure.h" +#include "./fuzztest/internal/domains/mutation_metadata.h" // IWYU pragma: export #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/printer.h" #include "./fuzztest/internal/serialization.h" -#include "./fuzztest/internal/table_of_recent_compares.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -73,7 +73,8 @@ namespace fuzztest::domain_implementor { // may first call `MaybeGetRandomSeed()` and return its non-nullopt result. // TODO(b/303324603): Move the call to `MaybeGetRandomSeed()` to a wrapper. // -// - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) +// - void Mutate(corpus_type& val, absl::BitGenRef prng, +// const MutationMetadata& metadata, bool only_shrink) // // The method that mutates the corpus value. // @@ -130,6 +131,15 @@ class DomainBase { } } + // Mutates `corpus_value` using `prng`, `only_shirnk` and the default mutation + // metadata. This is a temporary wrapper that redirects the call to the real + // interface with an explicit argument for metadata. + void Mutate(corpus_type& corpus_value, absl::BitGenRef prng, + bool only_shrink) { + return derived().Mutate(corpus_value, prng, MutationMetadata{}, + only_shrink); + } + // Returns a random user value from the domain. In general, doesn't provide // guarantees on the distribution of the returned values. // @@ -165,11 +175,12 @@ class DomainBase { return internal::IRObject::FromCorpus(v); } - void UpdateMemoryDictionary(const CorpusType& val) {} + void UpdateMemoryDictionary(const CorpusType& val, ConstCmpTablesPtr) {} uint64_t CountNumberOfFields(const CorpusType&) { return 0; } - uint64_t MutateSelectedField(CorpusType&, absl::BitGenRef, bool, uint64_t) { + uint64_t MutateSelectedField(CorpusType&, absl::BitGenRef, + const MutationMetadata&, bool, uint64_t) { return 0; } @@ -240,7 +251,8 @@ class DomainBase { // probability. constexpr int kMaxMutations = 1000; for (int i = absl::Uniform(prng, 0, kMaxMutations); i > 0; --i) { - derived().Mutate(corpus_val, prng, /*only_shrink=*/false); + derived().Mutate(corpus_val, prng, MutationMetadata{}, + /*only_shrink=*/false); } return corpus_val; } diff --git a/fuzztest/internal/domains/domain_type_erasure.h b/fuzztest/internal/domains/domain_type_erasure.h index c5b5b2a3..c2dfb83c 100644 --- a/fuzztest/internal/domains/domain_type_erasure.h +++ b/fuzztest/internal/domains/domain_type_erasure.h @@ -34,6 +34,7 @@ #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "./fuzztest/internal/any.h" +#include "./fuzztest/internal/domains/mutation_metadata.h" #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/printer.h" @@ -56,10 +57,13 @@ class UntypedDomainConcept { virtual std::unique_ptr UntypedClone() const = 0; virtual GenericDomainCorpusType UntypedInit(absl::BitGenRef) = 0; - virtual void UntypedMutate(GenericDomainCorpusType& val, absl::BitGenRef prng, - bool only_shrink) = 0; + virtual void UntypedMutate( + GenericDomainCorpusType& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) = 0; virtual void UntypedUpdateMemoryDictionary( - const GenericDomainCorpusType& val) = 0; + const GenericDomainCorpusType& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) = 0; virtual std::optional UntypedParseCorpus( const IRObject& obj) const = 0; virtual absl::Status UntypedValidateCorpusValue( @@ -68,9 +72,9 @@ class UntypedDomainConcept { const GenericDomainCorpusType& v) const = 0; virtual uint64_t UntypedCountNumberOfFields( const GenericDomainCorpusType&) = 0; - virtual uint64_t UntypedMutateSelectedField(GenericDomainCorpusType&, - absl::BitGenRef, bool, - uint64_t) = 0; + virtual uint64_t UntypedMutateSelectedField( + GenericDomainCorpusType&, absl::BitGenRef, + const domain_implementor::MutationMetadata&, bool, uint64_t) = 0; virtual GenericDomainValueType UntypedGetValue( const GenericDomainCorpusType& v) const = 0; virtual void UntypedPrintCorpusValue( @@ -135,12 +139,15 @@ class DomainModel final : public TypedDomainConcept> { } void UntypedMutate(GenericDomainCorpusType& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, bool only_shrink) final { - domain_.Mutate(val.GetAs(), prng, only_shrink); + domain_.Mutate(val.GetAs(), prng, metadata, only_shrink); } - void UntypedUpdateMemoryDictionary(const GenericDomainCorpusType& val) final { - domain_.UpdateMemoryDictionary(val.GetAs()); + void UntypedUpdateMemoryDictionary( + const GenericDomainCorpusType& val, + domain_implementor::ConstCmpTablesPtr cmp_tables) final { + domain_.UpdateMemoryDictionary(val.GetAs(), cmp_tables); } ValueType TypedGetRandomValue(absl::BitGenRef prng) final { @@ -185,11 +192,12 @@ class DomainModel final : public TypedDomainConcept> { return domain_.CountNumberOfFields(v.GetAs()); } - uint64_t UntypedMutateSelectedField(GenericDomainCorpusType& v, - absl::BitGenRef prng, bool only_shrink, - uint64_t selected_field_index) final { - return domain_.MutateSelectedField(v.GetAs(), prng, only_shrink, - selected_field_index); + uint64_t UntypedMutateSelectedField( + GenericDomainCorpusType& v, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, bool only_shrink, + uint64_t selected_field_index) final { + return domain_.MutateSelectedField(v.GetAs(), prng, metadata, + only_shrink, selected_field_index); } void UntypedPrintCorpusValue(const GenericDomainCorpusType& val, diff --git a/fuzztest/internal/domains/element_of_impl.h b/fuzztest/internal/domains/element_of_impl.h index 5ab31485..274b1e99 100644 --- a/fuzztest/internal/domains/element_of_impl.h +++ b/fuzztest/internal/domains/element_of_impl.h @@ -41,6 +41,8 @@ class ElementOfImpl using typename ElementOfImpl::DomainBase::corpus_type; using typename ElementOfImpl::DomainBase::value_type; + using ElementOfImpl::DomainBase::Mutate; + explicit ElementOfImpl(std::vector values) : values_(values) { FUZZTEST_INTERNAL_CHECK_PRECONDITION( !values.empty(), "ElementOf requires a non empty list."); @@ -51,7 +53,8 @@ class ElementOfImpl return corpus_type{absl::Uniform(prng, 0, values_.size())}; } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata&, bool only_shrink) { if (values_.size() <= 1) return; if (only_shrink) { size_t index = static_cast(val); diff --git a/fuzztest/internal/domains/filter_impl.h b/fuzztest/internal/domains/filter_impl.h index 346537cf..6b776200 100644 --- a/fuzztest/internal/domains/filter_impl.h +++ b/fuzztest/internal/domains/filter_impl.h @@ -37,6 +37,8 @@ class FilterImpl using typename FilterImpl::DomainBase::corpus_type; using typename FilterImpl::DomainBase::value_type; + using FilterImpl::DomainBase::Mutate; + FilterImpl() = default; explicit FilterImpl(std::function predicate, Domain inner) : predicate_(std::move(predicate)), inner_(std::move(inner)) {} @@ -49,10 +51,12 @@ class FilterImpl } } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { corpus_type original_val = val; while (true) { - inner_.Mutate(val, prng, only_shrink); + inner_.Mutate(val, prng, metadata, only_shrink); if (RunFilter(val)) return; val = original_val; } diff --git a/fuzztest/internal/domains/flat_map_impl.h b/fuzztest/internal/domains/flat_map_impl.h index 49cbc2b0..6d90abdf 100644 --- a/fuzztest/internal/domains/flat_map_impl.h +++ b/fuzztest/internal/domains/flat_map_impl.h @@ -60,6 +60,8 @@ class FlatMapImpl using typename FlatMapImpl::DomainBase::corpus_type; using typename FlatMapImpl::DomainBase::value_type; + using FlatMapImpl::DomainBase::Mutate; + FlatMapImpl() = default; explicit FlatMapImpl(FlatMapper flat_mapper, InputDomain... input_domains) : flat_mapper_(std::move(flat_mapper)), @@ -77,7 +79,9 @@ class FlatMapImpl input_corpus); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { // There is no way to tell whether the current output corpus value is // consistent with a new output domain generated by mutated inputs, so // mutating the inputs forces re-initialization of the output domain. This @@ -88,7 +92,7 @@ class FlatMapImpl ApplyIndex([&](auto... I) { // The first field of `val` is the output corpus value, so skip it. (std::get(input_domains_) - .Mutate(std::get(val), prng, only_shrink), + .Mutate(std::get(val), prng, metadata, only_shrink), ...); }); std::get<0>(val) = GetOutputDomain(val).Init(prng); @@ -99,7 +103,7 @@ class FlatMapImpl // convenience, not correctness. For example, `Filter` won't automatically // find when something is too restrictive. // TODO(b/246423623): Support stateful domains. - GetOutputDomain(val).Mutate(std::get<0>(val), prng, only_shrink); + GetOutputDomain(val).Mutate(std::get<0>(val), prng, metadata, only_shrink); } value_type GetValue(const corpus_type& v) const { diff --git a/fuzztest/internal/domains/in_grammar_impl.h b/fuzztest/internal/domains/in_grammar_impl.h index fc94a5a6..cdb15e23 100644 --- a/fuzztest/internal/domains/in_grammar_impl.h +++ b/fuzztest/internal/domains/in_grammar_impl.h @@ -104,7 +104,8 @@ class StringLiteralDomain { return ASTNode{id, std::monostate()}; } - static void Mutate(ASTNode& val, absl::BitGenRef prng, bool only_shrink) {} + static void Mutate(ASTNode& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata&, bool) {} static ASTTypeId TypeId() { return id; } @@ -147,8 +148,11 @@ class RegexLiteralDomain { return ASTNode{id, GetInnerRegexpDomain().Init(prng)}; } - static void Mutate(ASTNode& val, absl::BitGenRef prng, bool only_shrink) { - GetInnerRegexpDomain().Mutate(std::get<1>(val.children), prng, only_shrink); + static void Mutate(ASTNode& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + GetInnerRegexpDomain().Mutate(std::get<1>(val.children), prng, metadata, + only_shrink); } static ASTTypeId TypeId() { return id; } @@ -233,7 +237,9 @@ class VectorDomain { static ASTTypeId TypeId() { return id; } - static void Mutate(ASTNode& val, absl::BitGenRef prng, bool only_shrink) { + static void Mutate(ASTNode& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { FUZZTEST_INTERNAL_CHECK(val.children.index() == 2, "Not a vector!"); std::vector& elements = std::get<2>(val.children); if (only_shrink) { @@ -253,14 +259,14 @@ class VectorDomain { } else if (!can_change_element_num) { ElementT::Mutate( elements[absl::Uniform(prng, 0, elements.size())], prng, - only_shrink); + metadata, only_shrink); } else { if (absl::Bernoulli(prng, 0.5)) { ChangeElementNum(elements, prng); } else { ElementT::Mutate( elements[absl::Uniform(prng, 0, elements.size())], prng, - only_shrink); + metadata, only_shrink); } } } @@ -323,7 +329,7 @@ class VectorDomain { if (!can_remove_element) { // Cannot remove elements, let's shrink them. ElementT::Mutate(*ChoosePosition(elements, IncludeEnd::kNo, prng), prng, - true); + {}, /*only_shrink=*/true); } else if (!can_shrink_element) { // Cannot shrink elements, let's remove one. elements.erase(ChoosePosition(elements, IncludeEnd::kNo, prng)); @@ -334,7 +340,7 @@ class VectorDomain { } else { ElementT::Mutate(*ChoosePosition(elements, IncludeEnd::kNo, prng), prng, - true); + {}, /*only_shrink=*/true); } } } @@ -383,7 +389,9 @@ class TupleDomain { generation_budget / static_cast(sizeof...(ElementT)))...}}; } - static void Mutate(ASTNode& val, absl::BitGenRef prng, bool only_shrink) { + static void Mutate(ASTNode& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { FUZZTEST_INTERNAL_CHECK( val.children.index() == 2 && std::get<2>(val.children).size() == sizeof...(ElementT), @@ -404,7 +412,7 @@ class TupleDomain { int choice = mutables[absl::Uniform(prng, 0, mutables.size())]; ApplyIndex([&](auto... I) { ((choice == I ? (ElementT::Mutate(std::get<2>(val.children)[I], prng, - only_shrink)) + metadata, only_shrink)) : (void)0), ...); }); @@ -492,7 +500,9 @@ class VariantDomain { return result; } - static void Mutate(ASTNode& val, absl::BitGenRef prng, bool only_shrink) { + static void Mutate(ASTNode& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { constexpr bool has_alternative = sizeof...(ElementT) > 1; ASTNode current_value = std::get<2>(val.children).front(); ASTTypeId current_value_id = current_value.type_id; @@ -507,7 +517,7 @@ class VariantDomain { "Impossible at" + std::to_string(id)); if (only_shrink) { if (is_current_value_mutable) { - MutateCurrentValue(val, prng, only_shrink); + MutateCurrentValue(val, prng, metadata, only_shrink); } return; } @@ -515,10 +525,10 @@ class VariantDomain { if (!is_current_value_mutable) { SwitchToAlternative(val, prng); } else if (!has_alternative) { - MutateCurrentValue(val, prng, only_shrink); + MutateCurrentValue(val, prng, metadata, only_shrink); } else { if (absl::Bernoulli(prng, 0.5)) { - MutateCurrentValue(val, prng, only_shrink); + MutateCurrentValue(val, prng, metadata, only_shrink); } else { SwitchToAlternative(val, prng); } @@ -594,11 +604,12 @@ class VariantDomain { } private: - static void MutateCurrentValue(ASTNode& val, absl::BitGenRef prng, - bool only_shrink) { + static void MutateCurrentValue( + ASTNode& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, bool only_shrink) { ASTNode& current_value = std::get<2>(val.children).front(); ((ElementT::TypeId() == current_value.type_id - ? (ElementT::Mutate(current_value, prng, only_shrink)) + ? (ElementT::Mutate(current_value, prng, metadata, only_shrink)) : (void)0), ...); } @@ -640,17 +651,21 @@ class InGrammarImpl using typename InGrammarImpl::DomainBase::corpus_type; using typename InGrammarImpl::DomainBase::value_type; + using InGrammarImpl::DomainBase::Mutate; + ASTNode Init(absl::BitGenRef prng) { if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed; return TopDomain::Init(prng); } - void Mutate(ASTNode& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(ASTNode& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { if (only_shrink && absl::Bernoulli(prng, 0.5) && ShrinkByReplaceWithSubElementOfSameType(val, prng)) { return; } - TopDomain::Mutate(val, prng, only_shrink); + TopDomain::Mutate(val, prng, metadata, only_shrink); } auto GetPrinter() const { return StringPrinter{}; } diff --git a/fuzztest/internal/domains/in_range_impl.h b/fuzztest/internal/domains/in_range_impl.h index c0d52a76..fc7a04ef 100644 --- a/fuzztest/internal/domains/in_range_impl.h +++ b/fuzztest/internal/domains/in_range_impl.h @@ -29,13 +29,11 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" -#include "./fuzztest/internal/coverage.h" #include "./fuzztest/internal/domains/domain_base.h" #include "./fuzztest/internal/domains/special_values.h" #include "./fuzztest/internal/domains/value_mutation_helpers.h" #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/printer.h" -#include "./fuzztest/internal/table_of_recent_compares.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -45,6 +43,8 @@ class InRangeImpl : public domain_implementor::DomainBase> { public: using typename InRangeImpl::DomainBase::value_type; + using InRangeImpl::DomainBase::Mutate; + constexpr static bool T_is_integer = std::numeric_limits::is_integer; constexpr static bool T_is_signed = std::is_signed::value; constexpr static bool T_is_memory_dictionary_compatible = @@ -106,7 +106,9 @@ class InRangeImpl : public domain_implementor::DomainBase> { }); } - void Mutate(value_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(value_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { if (min_ == max_) { val = min_; return; @@ -146,9 +148,9 @@ class InRangeImpl : public domain_implementor::DomainBase> { val = absl::Uniform(absl::IntervalClosedClosed, prng, min_, max_); } } else { - RandomWalkOrUniformOrDict<5>(prng, val, min_, max_, temporary_dict_, - permanent_dict_, - permanent_dict_candidate_); + RandomWalkOrUniformOrDict<5>( + prng, val, min_, max_, metadata.cmp_tables, temporary_dict_, + permanent_dict_, permanent_dict_candidate_); } } else { if (absl::Bernoulli(prng, 0.25)) { @@ -157,14 +159,14 @@ class InRangeImpl : public domain_implementor::DomainBase> { val = absl::Uniform(absl::IntervalClosedClosed, prng, min_, max_); } } else { - RandomWalkOrUniformOrDict<5>(prng, val, min_, max_, temporary_dict_, - permanent_dict_, - permanent_dict_candidate_); + RandomWalkOrUniformOrDict<5>( + prng, val, min_, max_, metadata.cmp_tables, temporary_dict_, + permanent_dict_, permanent_dict_candidate_); } } } else { - RandomWalkOrUniformOrDict<5>(prng, val, min_, max_, temporary_dict_, - permanent_dict_, + RandomWalkOrUniformOrDict<5>(prng, val, min_, max_, metadata.cmp_tables, + temporary_dict_, permanent_dict_, permanent_dict_candidate_); } } while (val == prev); // Make sure Mutate really mutates. @@ -198,12 +200,12 @@ class InRangeImpl : public domain_implementor::DomainBase> { } } - void UpdateMemoryDictionary(const value_type& val) { + void UpdateMemoryDictionary( + const value_type& val, domain_implementor::ConstCmpTablesPtr cmp_tables) { if constexpr (T_is_memory_dictionary_compatible) { - if (GetExecutionCoverage() != nullptr) { - temporary_dict_.MatchEntriesFromTableOfRecentCompares( - val, GetExecutionCoverage()->GetTablesOfRecentCompares(), min_, - max_); + if (cmp_tables != nullptr) { + temporary_dict_.MatchEntriesFromTableOfRecentCompares(val, *cmp_tables, + min_, max_); if (permanent_dict_candidate_.has_value() && permanent_dict_.Size() < kPermanentDictMaxSize) { permanent_dict_.AddEntry(std::move(*permanent_dict_candidate_)); diff --git a/fuzztest/internal/domains/in_regexp_impl.h b/fuzztest/internal/domains/in_regexp_impl.h index 108e1837..9d02cb34 100644 --- a/fuzztest/internal/domains/in_regexp_impl.h +++ b/fuzztest/internal/domains/in_regexp_impl.h @@ -44,6 +44,8 @@ class InRegexpImpl : public domain_implementor::DomainBase { public: + using InRegexpImpl::DomainBase::Mutate; + explicit InRegexpImpl(std::string_view regex_str) : regex_str_(regex_str), dfa_(RegexpDFA::Create(regex_str_)) {} @@ -59,7 +61,8 @@ class InRegexpImpl // Strategy: Parse the input string into a path in the DFA. Pick a node in the // path and random walk from the node until we reach an end state or go back // to the original path. - void Mutate(DFAPath& path, absl::BitGenRef prng, bool only_shrink) { + void Mutate(DFAPath& path, absl::BitGenRef prng, + const domain_implementor::MutationMetadata&, bool only_shrink) { if (only_shrink) { // Fast path to remove loop. if (absl::Bernoulli(prng, 0.5)) { diff --git a/fuzztest/internal/domains/map_impl.h b/fuzztest/internal/domains/map_impl.h index f4a57e26..4d367b54 100644 --- a/fuzztest/internal/domains/map_impl.h +++ b/fuzztest/internal/domains/map_impl.h @@ -42,6 +42,8 @@ class MapImpl using typename MapImpl::DomainBase::corpus_type; using typename MapImpl::DomainBase::value_type; + using MapImpl::DomainBase::Mutate; + MapImpl() = default; explicit MapImpl(Mapper mapper, Inner... inner) : mapper_(std::move(mapper)), inner_(std::move(inner)...) {} @@ -58,9 +60,13 @@ class MapImpl inner_); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { return ApplyIndex([&](auto... I) { - (std::get(inner_).Mutate(std::get(val), prng, only_shrink), ...); + (std::get(inner_).Mutate(std::get(val), prng, metadata, + only_shrink), + ...); }); } @@ -117,6 +123,8 @@ class ReversibleMapImpl using typename ReversibleMapImpl::DomainBase::corpus_type; using typename ReversibleMapImpl::DomainBase::value_type; + using ReversibleMapImpl::DomainBase::Mutate; + static_assert( std::is_invocable_v && std::is_same_v, @@ -135,9 +143,13 @@ class ReversibleMapImpl inner_); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { return ApplyIndex([&](auto... I) { - (std::get(inner_).Mutate(std::get(val), prng, only_shrink), ...); + (std::get(inner_).Mutate(std::get(val), prng, metadata, + only_shrink), + ...); }); } diff --git a/fuzztest/internal/domains/mutation_metadata.h b/fuzztest/internal/domains/mutation_metadata.h new file mode 100644 index 00000000..e329d970 --- /dev/null +++ b/fuzztest/internal/domains/mutation_metadata.h @@ -0,0 +1,33 @@ +// Copyright 2024 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_MUTATION_METADATA_H_ +#define FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_MUTATION_METADATA_H_ + +#include "./fuzztest/internal/table_of_recent_compares.h" + +namespace fuzztest::domain_implementor { + +// Opaque pointer to comparison tables used by builtin domains, imported here +// for domain implementors who do not need the internal header. +using ConstCmpTablesPtr = const internal::TablesOfRecentCompares*; + +// A struct holding metadata for domain mutation. +struct MutationMetadata { + ConstCmpTablesPtr cmp_tables = nullptr; +}; + +} // namespace fuzztest::domain_implementor + +#endif // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_MUTATION_METADATA_H_ diff --git a/fuzztest/internal/domains/one_of_impl.h b/fuzztest/internal/domains/one_of_impl.h index aeb72fdf..d95ac3bf 100644 --- a/fuzztest/internal/domains/one_of_impl.h +++ b/fuzztest/internal/domains/one_of_impl.h @@ -42,6 +42,8 @@ class OneOfImpl using typename OneOfImpl::DomainBase::corpus_type; using typename OneOfImpl::DomainBase::value_type; + using OneOfImpl::DomainBase::Mutate; + // All value_types of inner domains must be the same. (Though note that they // can have different corpus_types!) static_assert( @@ -61,7 +63,9 @@ class OneOfImpl }); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { // Switch to another domain 1% of the time when not reducing. if (kNumDomains > 1 && !only_shrink && absl::Bernoulli(prng, 0.01)) { // Choose a different index. @@ -76,7 +80,7 @@ class OneOfImpl } else { Switch(val.index(), [&](auto I) { auto& domain = std::get(domains_); - domain.Mutate(std::get(val), prng, only_shrink); + domain.Mutate(std::get(val), prng, metadata, only_shrink); }); } } diff --git a/fuzztest/internal/domains/optional_of_impl.h b/fuzztest/internal/domains/optional_of_impl.h index 28dc2bef..7b6f728b 100644 --- a/fuzztest/internal/domains/optional_of_impl.h +++ b/fuzztest/internal/domains/optional_of_impl.h @@ -53,6 +53,8 @@ class OptionalOfImpl using typename OptionalOfImpl::DomainBase::corpus_type; using typename OptionalOfImpl::DomainBase::value_type; + using OptionalOfImpl::DomainBase::Mutate; + static_assert(Requires([](auto x) -> std::void_t { }), @@ -74,7 +76,9 @@ class OptionalOfImpl } } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { if (policy_ == OptionalPolicy::kAlwaysNull) { val.template emplace<0>(); return; @@ -88,7 +92,7 @@ class OptionalOfImpl // 1/100 chance of returning an empty. val.template emplace<0>(); } else { - inner_.Mutate(std::get<1>(val), prng, only_shrink); + inner_.Mutate(std::get<1>(val), prng, metadata, only_shrink); } } @@ -155,12 +159,13 @@ class OptionalOfImpl return 0; } - uint64_t MutateSelectedField(corpus_type& val, absl::BitGenRef prng, - bool only_shrink, - uint64_t selected_field_index) { + uint64_t MutateSelectedField( + corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, bool only_shrink, + uint64_t selected_field_index) { if (val.index() == 1) { - return inner_.MutateSelectedField(std::get<1>(val), prng, only_shrink, - selected_field_index); + return inner_.MutateSelectedField(std::get<1>(val), prng, metadata, + only_shrink, selected_field_index); } return 0; } diff --git a/fuzztest/internal/domains/overlap_of_impl.h b/fuzztest/internal/domains/overlap_of_impl.h index 65137b24..2c10cfce 100644 --- a/fuzztest/internal/domains/overlap_of_impl.h +++ b/fuzztest/internal/domains/overlap_of_impl.h @@ -45,6 +45,8 @@ class OverlapOfImpl using typename OverlapOfImpl::DomainBase::corpus_type; using typename OverlapOfImpl::DomainBase::value_type; + using OverlapOfImpl::DomainBase::Mutate; + static_assert( std::conjunction_v>...>, "All overlapping domains must have the same value_type."); @@ -65,7 +67,9 @@ class OverlapOfImpl } } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { const size_t index = val.index(); std::optional orig_value; std::optional mutant_corpus; @@ -85,7 +89,7 @@ class OverlapOfImpl FUZZTEST_INTERNAL_CHECK(inner_corpus.has_value(), "Mutate() called on a user value that is not " "valid in all overlapping domains"); - domain.Mutate(*inner_corpus, prng, only_shrink); + domain.Mutate(*inner_corpus, prng, metadata, only_shrink); mutant_corpus = corpus_type(std::in_place_index, *std::move(inner_corpus)); }); @@ -93,7 +97,8 @@ class OverlapOfImpl mutant_corpus = val; Switch(index, [&](auto I) { auto& domain = std::get(domains_); - domain.Mutate(std::get(*mutant_corpus), prng, only_shrink); + domain.Mutate(std::get(*mutant_corpus), prng, metadata, + only_shrink); }); } FUZZTEST_INTERNAL_CHECK(mutant_corpus.has_value(), diff --git a/fuzztest/internal/domains/protobuf_domain_impl.h b/fuzztest/internal/domains/protobuf_domain_impl.h index 6b1a024a..3a8682c8 100644 --- a/fuzztest/internal/domains/protobuf_domain_impl.h +++ b/fuzztest/internal/domains/protobuf_domain_impl.h @@ -431,6 +431,8 @@ class ProtobufDomainUntypedImpl using typename ProtobufDomainUntypedImpl::DomainBase::corpus_type; using typename ProtobufDomainUntypedImpl::DomainBase::value_type; + using ProtobufDomainUntypedImpl::DomainBase::Mutate; + explicit ProtobufDomainUntypedImpl(PrototypePtr prototype, bool use_lazy_initialization) : prototype_(std::move(prototype)), @@ -486,13 +488,15 @@ class ProtobufDomainUntypedImpl return val; } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { if (GetFieldCount(prototype_.Get()->GetDescriptor()) == 0) return; // TODO(JunyangShao): Maybe make CountNumberOfFields static. uint64_t total_weight = CountNumberOfFields(val); uint64_t selected_weight = absl::Uniform(absl::IntervalClosedClosed, prng, uint64_t{1}, total_weight); - MutateSelectedField(val, prng, only_shrink, selected_weight); + MutateSelectedField(val, prng, metadata, only_shrink, selected_weight); } auto GetPrinter() const { return ProtobufPrinter{}; } @@ -611,9 +615,10 @@ class ProtobufDomainUntypedImpl return total_weight; } - uint64_t MutateSelectedField(corpus_type& val, absl::BitGenRef prng, - bool only_shrink, - uint64_t selected_field_index) { + uint64_t MutateSelectedField( + corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, bool only_shrink, + uint64_t selected_field_index) { uint64_t field_counter = 0; auto descriptor = prototype_.Get()->GetDescriptor(); if (GetFieldCount(descriptor) == 0) return field_counter; @@ -625,7 +630,8 @@ class ProtobufDomainUntypedImpl } ++field_counter; if (field_counter == selected_field_index) { - VisitProtobufField(field, MutateVisitor{prng, only_shrink, *this, val}); + VisitProtobufField( + field, MutateVisitor{prng, metadata, only_shrink, *this, val}); return field_counter; } @@ -635,12 +641,12 @@ class ProtobufDomainUntypedImpl if (field->is_repeated()) { field_counter += GetSubDomain(field).MutateSelectedField( - val_it->second, prng, only_shrink, + val_it->second, prng, metadata, only_shrink, selected_field_index - field_counter); } else { field_counter += GetSubDomain(field).MutateSelectedField( - val_it->second, prng, only_shrink, + val_it->second, prng, metadata, only_shrink, selected_field_index - field_counter); } } @@ -947,6 +953,7 @@ class ProtobufDomainUntypedImpl struct MutateVisitor { absl::BitGenRef prng; + const domain_implementor::MutationMetadata& metadata; bool only_shrink; ProtobufDomainUntypedImpl& self; corpus_type& val; @@ -959,7 +966,7 @@ class ProtobufDomainUntypedImpl if (is_present) { // Mutate the element - domain.Mutate(it->second, prng, only_shrink); + domain.Mutate(it->second, prng, metadata, only_shrink); return; } @@ -997,7 +1004,7 @@ class ProtobufDomainUntypedImpl // Switch the inner domain for maps to use flat_hash_map instead. corpus_type corpus_copy; auto& copy = corpus_copy[field->number()] = it->second; - domain.Mutate(copy, prng, only_shrink); + domain.Mutate(copy, prng, metadata, only_shrink); auto v = self.GetValue(corpus_copy); // We need to roundtrip through serialization to really dedup. The // reflection API alone doesn't cut it. @@ -1008,7 +1015,7 @@ class ProtobufDomainUntypedImpl it->second = std::move(copy); } } else { - domain.Mutate(it->second, prng, only_shrink); + domain.Mutate(it->second, prng, metadata, only_shrink); } } }; @@ -1244,7 +1251,7 @@ class ProtobufDomainUntypedImpl auto val = domain.Init(gen); auto descriptor = GetDescriptor(val); for (int i = 0; !descriptor && i < kMaxTry; ++i) { - domain.Mutate(val, gen, /*only_shrink=*/false); + domain.Mutate(val, gen, {}, false); descriptor = GetDescriptor(val); } FUZZTEST_INTERNAL_CHECK_PRECONDITION( @@ -1789,6 +1796,8 @@ class ProtobufDomainImpl using typename ProtobufDomainImpl::DomainBase::value_type; using FieldDescriptor = ProtobufFieldDescriptor; + using ProtobufDomainImpl::DomainBase::Mutate; + corpus_type Init(absl::BitGenRef prng) { if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed; return inner_.Init(prng); @@ -1802,8 +1811,10 @@ class ProtobufDomainImpl return inner_.MutateNumberOfProtoFields(val); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { - inner_.Mutate(val, prng, only_shrink); + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + inner_.Mutate(val, prng, metadata, only_shrink); } value_type GetValue(const corpus_type& v) const { @@ -2239,13 +2250,16 @@ class ArbitraryImpl>> public: using typename ArbitraryImpl::DomainBase::value_type; + using ArbitraryImpl::DomainBase::Mutate; + value_type Init(absl::BitGenRef prng) { if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed; const int index = absl::Uniform(prng, 0, descriptor()->value_count()); return static_cast(descriptor()->value(index)->number()); } - void Mutate(value_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(value_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata&, bool only_shrink) { if (only_shrink) { std::vector numbers; for (int i = 0; i < descriptor()->value_count(); ++i) { diff --git a/fuzztest/internal/domains/smart_pointer_of_impl.h b/fuzztest/internal/domains/smart_pointer_of_impl.h index 2b3d53b7..ea57eb7d 100644 --- a/fuzztest/internal/domains/smart_pointer_of_impl.h +++ b/fuzztest/internal/domains/smart_pointer_of_impl.h @@ -47,6 +47,8 @@ class SmartPointerOfImpl using typename SmartPointerOfImpl::DomainBase::corpus_type; using typename SmartPointerOfImpl::DomainBase::value_type; + using SmartPointerOfImpl::DomainBase::Mutate; + static_assert( Requires( [](auto x) -> std::enable_if_t, @@ -67,7 +69,9 @@ class SmartPointerOfImpl return corpus_type(); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { const bool has_value = val.index() == 1; if (!has_value) { // Only add a value if we are not shrinking. @@ -76,7 +80,7 @@ class SmartPointerOfImpl // 1/100 chance of returning an empty. val.template emplace<0>(); } else { - GetOrMakeInner().Mutate(std::get<1>(val), prng, only_shrink); + GetOrMakeInner().Mutate(std::get<1>(val), prng, metadata, only_shrink); } } diff --git a/fuzztest/internal/domains/unique_elements_container_of_impl.h b/fuzztest/internal/domains/unique_elements_container_of_impl.h index 68423f1a..2f2aa621 100644 --- a/fuzztest/internal/domains/unique_elements_container_of_impl.h +++ b/fuzztest/internal/domains/unique_elements_container_of_impl.h @@ -52,6 +52,8 @@ class UniqueElementsContainerImpl using typename UniqueElementsContainerImpl::DomainBase::corpus_type; using typename UniqueElementsContainerImpl::DomainBase::value_type; + using UniqueElementsContainerImpl::DomainBase::Mutate; + UniqueElementsContainerImpl() = default; explicit UniqueElementsContainerImpl(InnerDomain inner) : unique_domain_(std::move(inner)) {} @@ -64,8 +66,10 @@ class UniqueElementsContainerImpl return unique_domain_.Init(prng); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { - unique_domain_.Mutate(val, prng, only_shrink); + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + unique_domain_.Mutate(val, prng, metadata, only_shrink); } value_type GetValue(const corpus_type& v) const { diff --git a/fuzztest/internal/domains/value_mutation_helpers.h b/fuzztest/internal/domains/value_mutation_helpers.h index fa5f7885..13179abd 100644 --- a/fuzztest/internal/domains/value_mutation_helpers.h +++ b/fuzztest/internal/domains/value_mutation_helpers.h @@ -23,7 +23,7 @@ #include "absl/numeric/int128.h" #include "absl/random/bit_gen_ref.h" #include "absl/random/random.h" -#include "./fuzztest/internal/coverage.h" +#include "./fuzztest/internal/domains/mutation_metadata.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/table_of_recent_compares.h" @@ -91,6 +91,7 @@ T SampleFromUniformRange(absl::BitGenRef prng, T min, T max) { // dictionary fails to mutate, fall back to uniform. template void RandomWalkOrUniformOrDict(absl::BitGenRef prng, T& val, T min, T max, + domain_implementor::ConstCmpTablesPtr cmp_tables, const IntegerDictionaryT& temporary_dict, const IntegerDictionaryT& permanent_dict, std::optional& permanent_dict_candidate) { @@ -98,8 +99,7 @@ void RandomWalkOrUniformOrDict(absl::BitGenRef prng, T& val, T min, T max, std::numeric_limits::is_integer && (sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8); const bool can_use_memory_dictionary = - is_memory_dictionary_compatible_integer && - GetExecutionCoverage() != nullptr; + is_memory_dictionary_compatible_integer && cmp_tables != nullptr; const int action_count = 2 + can_use_memory_dictionary; int action = absl::Uniform(prng, 0, action_count); // Random walk. @@ -143,9 +143,7 @@ void RandomWalkOrUniformOrDict(absl::BitGenRef prng, T& val, T min, T max, }, [&] { auto entry = IntegerDictionary::GetRandomTORCEntry( - val, prng, - GetExecutionCoverage()->GetTablesOfRecentCompares(), min, - max); + val, prng, *cmp_tables, min, max); if (entry.has_value()) { val = *entry; } else { diff --git a/fuzztest/internal/domains/variant_of_impl.h b/fuzztest/internal/domains/variant_of_impl.h index 5ee71f14..a7ef2827 100644 --- a/fuzztest/internal/domains/variant_of_impl.h +++ b/fuzztest/internal/domains/variant_of_impl.h @@ -43,6 +43,8 @@ class VariantOfImpl : public domain_implementor::DomainBase< using typename VariantOfImpl::DomainBase::corpus_type; using typename VariantOfImpl::DomainBase::value_type; + using VariantOfImpl::DomainBase::Mutate; + VariantOfImpl() = default; explicit VariantOfImpl(std::in_place_t, Inner... inner) : inner_(std::move(inner)...) {} @@ -56,7 +58,9 @@ class VariantOfImpl : public domain_implementor::DomainBase< }); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { // Flip a coin to choose between generating a value of an alternative type // and mutating the value of the current type. Assign more weight to the // mutating case in order to explore more on a given type before we start @@ -65,7 +69,8 @@ class VariantOfImpl : public domain_implementor::DomainBase< val = Init(prng); } else { Switch(val.index(), [&](auto I) { - std::get(inner_).Mutate(std::get(val), prng, only_shrink); + std::get(inner_).Mutate(std::get(val), prng, metadata, + only_shrink); }); } } diff --git a/fuzztest/internal/runtime.cc b/fuzztest/internal/runtime.cc index 029b7be4..423fe552 100644 --- a/fuzztest/internal/runtime.cc +++ b/fuzztest/internal/runtime.cc @@ -55,6 +55,7 @@ #include "./fuzztest/internal/configuration.h" #include "./fuzztest/internal/corpus_database.h" #include "./fuzztest/internal/coverage.h" +#include "./fuzztest/internal/domains/mutation_metadata.h" #include "./fuzztest/internal/fixture_driver.h" #include "./fuzztest/internal/flag_name.h" #include "./fuzztest/internal/io.h" @@ -79,6 +80,7 @@ inline constexpr int TRAP_PERF = 6; namespace fuzztest::internal { namespace { +using ::fuzztest::domain_implementor::MutationMetadata; using ::fuzztest::domain_implementor::PrintMode; using ::fuzztest::domain_implementor::RawSink; @@ -621,7 +623,7 @@ bool FuzzTestFuzzerImpl::ReplayInputsIfAvailable( while (!ShouldStop()) { auto copy = *to_minimize; for (int i = 0; i < num_mutations; ++i) { - params_domain_.Mutate(copy, prng, true); + params_domain_.Mutate(copy, prng, {}, true); } num_mutations = std::max(1, num_mutations - 1); // We compare the serialized version. Not very efficient but works for @@ -676,7 +678,8 @@ std::optional FuzzTestFuzzerImpl::ReadReproducerToMinimize() { return *reproducer; } -void FuzzTestFuzzerImpl::MutateValue(Input& input, absl::BitGenRef prng) { +void FuzzTestFuzzerImpl::MutateValue(Input& input, absl::BitGenRef prng, + const MutationMetadata& metadata) { // Do a random number of mutations on the value at once, skewed // towards 1 and decreasing probability as we go up. // Doing multiple smaller mutations at once allows reaching states that @@ -694,7 +697,7 @@ void FuzzTestFuzzerImpl::MutateValue(Input& input, absl::BitGenRef prng) { // optimized in any significant way. for (int mutations_at_once = absl::Poisson(prng) + 1; mutations_at_once > 0; --mutations_at_once) { - params_domain_.Mutate(input.args, prng, /* only_shrink= */ false); + params_domain_.Mutate(input.args, prng, metadata, /*only_shrink=*/false); } } @@ -762,7 +765,10 @@ void FuzzTestFuzzerImpl::TrySampleAndUpdateInMemoryCorpus(Input sample, auto [new_coverage, run_time] = TrySample(sample, write_to_file); if (execution_coverage_ != nullptr && (stats_.runs % 4096 == 0 || new_coverage)) { - params_domain_.UpdateMemoryDictionary(sample.args); + auto* coverage = GetExecutionCoverage(); + params_domain_.UpdateMemoryDictionary( + sample.args, + coverage == nullptr ? nullptr : &coverage->GetTablesOfRecentCompares()); } if (!new_coverage) return; // New coverage, update corpus and weights. @@ -959,7 +965,7 @@ void FuzzTestFuzzerImpl::RunInUnitTestMode(const Configuration& configuration) { // generate a new one through Init. constexpr size_t num_mutations_per_value = 100; if (i % num_mutations_per_value < num_mutations_per_value - 1) { - MutateValue(mutation, prng); + MutateValue(mutation, prng, {}); } else { mutation.args = params_domain_.Init(prng); } @@ -1041,7 +1047,7 @@ void FuzzTestFuzzerImpl::MinimizeNonFatalFailureLocally(absl::BitGenRef prng) { // reach another failure, but prefer a low number of mutations (thus Zipf). for (int num_mutations = absl::Zipf(prng, 10); num_mutations >= 0; --num_mutations) { - params_domain_.Mutate(copy.args, prng, /* only_shrink= */ true); + params_domain_.Mutate(copy.args, prng, {}, true); } // Only run it if it actually is different. Random mutations might // not actually change the value, or we have reached a minimum that can't be @@ -1165,6 +1171,10 @@ int FuzzTestFuzzerImpl::RunInFuzzingMode(int* /*argc*/, char*** /*argv*/, try_input_and_process_counterexample({params_domain_.Init(prng)}); } + MutationMetadata mutation_metadata; + if (auto* coverage = GetExecutionCoverage(); coverage != nullptr) { + mutation_metadata.cmp_tables = &coverage->GetTablesOfRecentCompares(); + } // Fuzz corpus elements in round robin fashion. while (!ShouldStop()) { Input input_to_mutate = [&]() -> Input { @@ -1184,7 +1194,7 @@ int FuzzTestFuzzerImpl::RunInFuzzingMode(int* /*argc*/, char*** /*argv*/, for (int i = 0; i < kMutationsPerInput; ++i) { if (ShouldStop()) break; Input mutation = input_to_mutate; - MutateValue(mutation, prng); + MutateValue(mutation, prng, mutation_metadata); try_input_and_process_counterexample(std::move(mutation)); } } diff --git a/fuzztest/internal/runtime.h b/fuzztest/internal/runtime.h index 83646a70..77bf8c0b 100644 --- a/fuzztest/internal/runtime.h +++ b/fuzztest/internal/runtime.h @@ -312,7 +312,8 @@ class FuzzTestFuzzerImpl : public FuzzTestFuzzer { absl::StatusOr TryParse(absl::string_view data); - void MutateValue(Input& input, absl::BitGenRef prng); + void MutateValue(Input& input, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata); void UpdateCorpusDistribution(); diff --git a/fuzztest/llvm_fuzzer_wrapper.cc b/fuzztest/llvm_fuzzer_wrapper.cc index 7b6c7485..bbdd7f3a 100644 --- a/fuzztest/llvm_fuzzer_wrapper.cc +++ b/fuzztest/llvm_fuzzer_wrapper.cc @@ -16,6 +16,9 @@ #include "./fuzztest/internal/domains/container_of_impl.h" #include "./fuzztest/internal/domains/domain_base.h" #include "./fuzztest/internal/io.h" +#ifndef FUZZTEST_USE_CENTIPEDE +#include "./fuzztest/internal/coverage.h" +#endif ABSL_DECLARE_FLAG(std::string, llvm_fuzzer_wrapper_dict_file); ABSL_DECLARE_FLAG(std::string, llvm_fuzzer_wrapper_corpus_dir); @@ -135,7 +138,12 @@ extern "C" size_t LLVMFuzzerMutate(uint8_t* data, size_t size, domain.WithMaxSize(max_size); absl::BitGen bitgen; InplaceVector val(data, size); - domain.Mutate(val, bitgen, false); + fuzztest::domain_implementor::MutationMetadata metadata; + if (auto* coverage = fuzztest::internal::GetExecutionCoverage(); + coverage != nullptr) { + metadata.cmp_tables = &coverage->GetTablesOfRecentCompares(); + } + domain.Mutate(val, bitgen, metadata, false); return val.size(); } @@ -151,14 +159,16 @@ class ArbitraryByteVector ArbitraryByteVector() { WithMaxSize(kByteArrayMaxLen); } - void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) { + void Mutate(corpus_type& val, absl::BitGenRef prng, + const fuzztest::domain_implementor::MutationMetadata& metadata, + bool only_shrink) { if (LLVMFuzzerCustomMutator) { const size_t size = val.size(); const size_t max_size = only_shrink ? size : kByteArrayMaxLen; val.resize(max_size); val.resize(LLVMFuzzerCustomMutator(val.data(), size, max_size, prng())); } else { - Base::Mutate(val, prng, only_shrink); + Base::Mutate(val, prng, metadata, only_shrink); } } };