Skip to content

Commit

Permalink
Explicitly use the comparison tables in domain methods.
Browse files Browse the repository at this point in the history
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: 691204568
  • Loading branch information
xinhaoyuan authored and copybara-github committed Oct 29, 2024
1 parent 84d901e commit 184807a
Show file tree
Hide file tree
Showing 51 changed files with 566 additions and 344 deletions.
2 changes: 1 addition & 1 deletion centipede/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
)

Expand Down
35 changes: 15 additions & 20 deletions centipede/fuzztest_mutator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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<uint8_t>())) {
if (fuzztest::internal::GetExecutionCoverage() == nullptr) {
execution_coverage_ = std::make_unique<ExecutionCoverage>(
/*counter_map=*/absl::Span<uint8_t>{});
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<ExecutionCoverage> execution_coverage_;
};

FuzzTestMutator::FuzzTestMutator(const Knobs &knobs, uint64_t seed)
: knobs_(knobs), prng_(seed), domain_(std::make_unique<MutatorDomain>()) {
: knobs_(knobs),
prng_(seed),
mutation_metadata_(std::make_unique<MutationMetadata>()),
domain_(std::make_unique<MutatorDomain>()) {
domain_->WithMinSize(1).WithMaxSize(max_len_);
}

Expand Down Expand Up @@ -123,23 +118,23 @@ void FuzzTestMutator::MutateMany(const std::vector<MutationInputRef>& inputs,
inputs[absl::Uniform<size_t>(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);
});
}

Expand Down
3 changes: 3 additions & 0 deletions centipede/fuzztest_mutator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -72,6 +73,8 @@ class FuzzTestMutator {
const Knobs &knobs_;
Rng prng_;
size_t max_len_ = 1000;

std::unique_ptr<MutationMetadata> mutation_metadata_;
std::unique_ptr<MutatorDomain> domain_;
};

Expand Down
3 changes: 2 additions & 1 deletion domain_tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
Expand All @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion domain_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -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
Expand Down
22 changes: 11 additions & 11 deletions domain_tests/aggregate_combinators_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ TEST(StructOf, MutateGeneratesValidValues) {
Set<std::string> 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);
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -151,7 +151,7 @@ TEST(ConstructorOf, MutateGeneratesValidValues) {
Set<std::string> 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());
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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),
Expand All @@ -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);
}
}

Expand All @@ -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);
}
}

Expand Down
30 changes: 15 additions & 15 deletions domain_tests/arbitrary_domains_protobuf_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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())
Expand All @@ -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())
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}

Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -616,9 +616,9 @@ TEST(ProtocolBufferEnum, Arbitrary) {
Set<TestProtobuf_Enum> 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) {
Expand Down
Loading

0 comments on commit 184807a

Please sign in to comment.