From caa39841be25c24d8c184e38ece0fac10d4f451a Mon Sep 17 00:00:00 2001 From: Xinhao Yuan Date: Mon, 14 Oct 2024 14:54:44 -0700 Subject: [PATCH] Explicitly use the comparison tables in domain methods. This is to remove the implicit dependency from domain mutation to the legacy fuzzing engine. For now, keep the old Mutate interface in DomainBase to make ad-hoc Mutate() calls happy (with best efforts). But it will be removed soon. PiperOrigin-RevId: 685844763 --- centipede/BUILD | 2 +- centipede/fuzztest_mutator.cc | 35 ++++----- centipede/fuzztest_mutator.h | 3 + domain_tests/BUILD | 3 +- domain_tests/CMakeLists.txt | 3 +- domain_tests/aggregate_combinators_test.cc | 22 +++--- .../arbitrary_domains_protobuf_test.cc | 30 ++++---- domain_tests/arbitrary_domains_test.cc | 16 ++-- domain_tests/container_combinators_test.cc | 2 +- domain_tests/container_test.cc | 32 +++----- domain_tests/domain_testing.h | 47 +++++++----- domain_tests/in_grammar_domain_test.cc | 6 +- domain_tests/in_regexp_domain_test.cc | 6 +- domain_tests/map_filter_combinator_test.cc | 8 +- domain_tests/misc_domains_test.cc | 10 +-- domain_tests/numeric_domains_test.cc | 6 +- domain_tests/pointer_domains_test.cc | 2 +- domain_tests/recursive_domains_test.cc | 4 +- domain_tests/specific_value_domains_test.cc | 8 +- domain_tests/string_domains_test.cc | 2 +- fuzztest/BUILD | 6 +- fuzztest/CMakeLists.txt | 4 +- fuzztest/domain_core.h | 24 ++++-- fuzztest/internal/centipede_adaptor.cc | 31 ++------ fuzztest/internal/domains/aggregate_of_impl.h | 14 +++- fuzztest/internal/domains/arbitrary_impl.h | 75 +++++++++++++------ .../domains/bit_flag_combination_of_impl.h | 8 +- .../domains/container_mutation_helpers.h | 10 +-- fuzztest/internal/domains/container_of_impl.h | 53 +++++++------ fuzztest/internal/domains/domain.h | 52 +++++++++---- fuzztest/internal/domains/domain_base.h | 22 ++++-- .../internal/domains/domain_type_erasure.h | 36 +++++---- fuzztest/internal/domains/element_of_impl.h | 5 +- fuzztest/internal/domains/filter_impl.h | 8 +- fuzztest/internal/domains/flat_map_impl.h | 10 ++- fuzztest/internal/domains/in_grammar_impl.h | 53 ++++++++----- fuzztest/internal/domains/in_range_impl.h | 34 +++++---- fuzztest/internal/domains/in_regexp_impl.h | 5 +- fuzztest/internal/domains/map_impl.h | 20 ++++- fuzztest/internal/domains/mutation_metadata.h | 33 ++++++++ fuzztest/internal/domains/one_of_impl.h | 8 +- fuzztest/internal/domains/optional_of_impl.h | 19 +++-- fuzztest/internal/domains/overlap_of_impl.h | 11 ++- .../internal/domains/protobuf_domain_impl.h | 44 +++++++---- .../internal/domains/smart_pointer_of_impl.h | 8 +- .../unique_elements_container_of_impl.h | 8 +- .../internal/domains/value_mutation_helpers.h | 10 +-- fuzztest/internal/domains/variant_of_impl.h | 9 ++- fuzztest/internal/runtime.cc | 24 ++++-- fuzztest/internal/runtime.h | 3 +- fuzztest/llvm_fuzzer_wrapper.cc | 16 +++- 51 files changed, 566 insertions(+), 344 deletions(-) create mode 100644 fuzztest/internal/domains/mutation_metadata.h diff --git a/centipede/BUILD b/centipede/BUILD index 5bb99e3e..b276a259 100644 --- a/centipede/BUILD +++ b/centipede/BUILD @@ -931,8 +931,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 65dcf275..c6f58888 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 5091c512..92f2878d 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 6b3b4914..998635a7 100644 --- a/fuzztest/internal/runtime.cc +++ b/fuzztest/internal/runtime.cc @@ -57,6 +57,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" @@ -81,6 +82,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; @@ -655,7 +657,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 @@ -710,7 +712,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 @@ -728,7 +731,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); } } @@ -796,7 +799,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. @@ -993,7 +999,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); } @@ -1075,7 +1081,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 @@ -1199,6 +1205,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 { @@ -1218,7 +1228,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 7f919450..89fd494a 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); } } };