Skip to content

Commit 8491407

Browse files
xinhaoyuancopybara-github
authored andcommitted
Unify the metadata handling between the Centipede builtin mutator and the adaptor mutator.
Also, use a class member of vector<optional<TORC>> instead of per-function vector<unique_ptr<TORC>> to reduce dynamic memory allocations. Note that the mutation functions are still separate due to the differences that the builtin mutator supports crossover while the adaptor mutator generates init inputs at a small chance. PiperOrigin-RevId: 816786388
1 parent 850f6ea commit 8491407

File tree

5 files changed

+77
-76
lines changed

5 files changed

+77
-76
lines changed

centipede/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ cc_library(
926926
"@abseil-cpp//absl/random",
927927
"@abseil-cpp//absl/types:span",
928928
"@com_google_fuzztest//common:defs",
929+
"@com_google_fuzztest//common:logging",
929930
"@com_google_fuzztest//fuzztest:domain_core",
930931
"@com_google_fuzztest//fuzztest/internal:table_of_recent_compares",
931932
],

centipede/fuzztest_mutator.cc

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#include <cstddef>
1919
#include <cstdint>
2020
#include <cstdlib>
21+
#include <cstring>
2122
#include <memory>
23+
#include <optional>
2224
#include <utility>
2325
#include <vector>
2426

@@ -29,6 +31,7 @@
2931
#include "./centipede/knobs.h"
3032
#include "./centipede/mutation_input.h"
3133
#include "./common/defs.h"
34+
#include "./common/logging.h"
3235
#include "./fuzztest/domain_core.h"
3336
#include "./fuzztest/internal/table_of_recent_compares.h"
3437

@@ -39,10 +42,48 @@ namespace {
3942
using MutatorDomainBase =
4043
decltype(fuzztest::VectorOf(fuzztest::Arbitrary<uint8_t>()));
4144

45+
template <typename T>
46+
void InsertCmpEntryIntoIntegerDictionary(const uint8_t* a, const uint8_t* b,
47+
TablesOfRecentCompares& cmp_tables) {
48+
T a_int;
49+
T b_int;
50+
std::memcpy(&a_int, a, sizeof(T));
51+
std::memcpy(&b_int, b, sizeof(T));
52+
cmp_tables.GetMutable<sizeof(T)>().Insert(a_int, b_int);
53+
}
54+
4255
} // namespace
4356

57+
void PopulateCmpEntries(const ExecutionMetadata& metadata,
58+
TablesOfRecentCompares& cmp_tables) {
59+
// Size limits on the cmp entries to be populated.
60+
static constexpr uint8_t kMaxCmpEntrySize = 15;
61+
static constexpr uint8_t kMinCmpEntrySize = 2;
62+
63+
metadata.ForEachCmpEntry([&cmp_tables](fuzztest::internal::ByteSpan a,
64+
fuzztest::internal::ByteSpan b) {
65+
FUZZTEST_CHECK(a.size() == b.size())
66+
<< "cmp operands must have the same size";
67+
const size_t size = a.size();
68+
if (size < kMinCmpEntrySize) return;
69+
if (size > kMaxCmpEntrySize) return;
70+
if (size == 2) {
71+
InsertCmpEntryIntoIntegerDictionary<uint16_t>(a.data(), b.data(),
72+
cmp_tables);
73+
} else if (size == 4) {
74+
InsertCmpEntryIntoIntegerDictionary<uint32_t>(a.data(), b.data(),
75+
cmp_tables);
76+
} else if (size == 8) {
77+
InsertCmpEntryIntoIntegerDictionary<uint64_t>(a.data(), b.data(),
78+
cmp_tables);
79+
}
80+
cmp_tables.GetMutable<0>().Insert(a.data(), b.data(), size);
81+
});
82+
}
83+
4484
struct FuzzTestMutator::MutationMetadata {
45-
fuzztest::internal::TablesOfRecentCompares cmp_tables;
85+
std::vector<std::optional<fuzztest::internal::TablesOfRecentCompares>>
86+
cmp_tables;
4687
};
4788

4889
class FuzzTestMutator::MutatorDomain : public MutatorDomainBase {
@@ -101,42 +142,36 @@ void FuzzTestMutator::CrossOver(ByteArray &data, const ByteArray &other) {
101142
std::vector<ByteArray> FuzzTestMutator::MutateMany(
102143
const std::vector<MutationInputRef> &inputs, size_t num_mutants) {
103144
if (inputs.empty()) abort();
104-
// TODO(xinhaoyuan): Consider metadata in other inputs instead of always the
105-
// first one.
106-
SetMetadata(inputs[0].metadata != nullptr ? *inputs[0].metadata
107-
: ExecutionMetadata());
145+
auto& cmp_tables = mutation_metadata_->cmp_tables;
146+
cmp_tables.resize(inputs.size());
108147
std::vector<ByteArray> mutants;
109148
mutants.reserve(num_mutants);
110149
for (int i = 0; i < num_mutants; ++i) {
111-
auto mutant = inputs[absl::Uniform<size_t>(prng_, 0, inputs.size())].data;
150+
auto index = absl::Uniform<size_t>(prng_, 0, inputs.size());
151+
if (!cmp_tables[index].has_value() && inputs[index].metadata != nullptr) {
152+
cmp_tables[index].emplace(/*compact=*/true);
153+
PopulateCmpEntries(*inputs[index].metadata, *cmp_tables[index]);
154+
}
155+
auto mutant = inputs[index].data;
112156
if (mutant.size() > max_len_) mutant.resize(max_len_);
113157
if (knobs_.GenerateBool(knob_mutate_or_crossover, prng_())) {
114158
// Perform crossover with some other input. It may be the same input.
115159
const auto &other_input =
116160
inputs[absl::Uniform<size_t>(prng_, 0, inputs.size())].data;
117161
CrossOver(mutant, other_input);
118162
} else {
119-
domain_->Mutate(mutant, prng_,
120-
{/*cmp_tables=*/&mutation_metadata_->cmp_tables},
121-
/*only_shrink=*/false);
163+
domain_->Mutate(
164+
mutant, prng_,
165+
{/*cmp_tables=*/cmp_tables[index].has_value() ? &*cmp_tables[index]
166+
: nullptr},
167+
/*only_shrink=*/false);
122168
}
123169
mutants.push_back(std::move(mutant));
124170
}
171+
cmp_tables.clear();
125172
return mutants;
126173
}
127174

128-
void FuzzTestMutator::SetMetadata(const ExecutionMetadata &metadata) {
129-
metadata.ForEachCmpEntry([this](ByteSpan a, ByteSpan b) {
130-
size_t size = a.size();
131-
if (size < kMinCmpEntrySize) return;
132-
if (size > kMaxCmpEntrySize) return;
133-
// Use the memcmp table to avoid subtlety of the container domain mutation
134-
// with integer tables. E.g. it won't insert integer comparison data.
135-
mutation_metadata_->cmp_tables.GetMutable<0>().Insert(a.data(), b.data(),
136-
size);
137-
});
138-
}
139-
140175
bool FuzzTestMutator::set_max_len(size_t max_len) {
141176
max_len_ = max_len;
142177
domain_->WithMaxSize(max_len);

centipede/fuzztest_mutator.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@
2424
#include "./centipede/knobs.h"
2525
#include "./centipede/mutation_input.h"
2626
#include "./common/defs.h"
27+
#include "./fuzztest/internal/table_of_recent_compares.h"
2728

2829
namespace fuzztest::internal {
2930

31+
// Populates comparison entries in `metadata` to `cmp_tables`.
32+
void PopulateCmpEntries(const ExecutionMetadata& metadata,
33+
TablesOfRecentCompares& cmp_tables);
34+
3035
// Mutator based on the FuzzTest std::vector domain. It always
3136
// generates non-empty results, with a default limit on the mutant
3237
// size unless changed by `set_max_len`.
@@ -56,19 +61,12 @@ class FuzzTestMutator {
5661
struct MutationMetadata;
5762
class MutatorDomain;
5863

59-
// Propagates the execution `metadata` to the internal mutation dictionary.
60-
void SetMetadata(const ExecutionMetadata& metadata);
61-
6264
// The crossover algorithm based on the legacy ByteArrayMutator.
6365
// TODO(ussuri): Implement and use the domain level crossover.
6466
void CrossOverInsert(ByteArray &data, const ByteArray &other);
6567
void CrossOverOverwrite(ByteArray &data, const ByteArray &other);
6668
void CrossOver(ByteArray &data, const ByteArray &other);
6769

68-
// Size limits on the cmp entries to be used in mutation.
69-
static constexpr uint8_t kMaxCmpEntrySize = 15;
70-
static constexpr uint8_t kMinCmpEntrySize = 2;
71-
7270
const Knobs &knobs_;
7371
Rng prng_;
7472
size_t max_len_ = 1000;

fuzztest/internal/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ cc_library(
7777
"@com_google_fuzztest//centipede:centipede_runner_no_main",
7878
"@com_google_fuzztest//centipede:environment",
7979
"@com_google_fuzztest//centipede:execution_metadata",
80+
"@com_google_fuzztest//centipede:fuzztest_mutator",
8081
"@com_google_fuzztest//centipede:mutation_input",
8182
"@com_google_fuzztest//centipede:runner_result",
8283
"@com_google_fuzztest//centipede:stop",

fuzztest/internal/centipede_adaptor.cc

Lines changed: 14 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#include "./centipede/centipede_interface.h"
7070
#include "./centipede/environment.h"
7171
#include "./centipede/execution_metadata.h"
72+
#include "./centipede/fuzztest_mutator.h"
7273
#include "./centipede/mutation_input.h"
7374
#include "./centipede/runner_interface.h"
7475
#include "./centipede/runner_result.h"
@@ -524,8 +525,8 @@ class CentipedeAdaptorRunnerCallbacks
524525
std::function<void(fuzztest::internal::ByteSpan)>
525526
new_mutant_callback) override {
526527
if (inputs.empty()) return false;
527-
std::vector<std::unique_ptr<TablesOfRecentCompares>> input_cmp_tables(
528-
inputs.size());
528+
cmp_tables.resize(inputs.size());
529+
absl::Cleanup cmp_tables_cleaner = [this]() { cmp_tables.clear(); };
529530
for (size_t i = 0; i < num_mutants; ++i) {
530531
const auto choice = absl::Uniform<double>(prng_, 0, 1);
531532
std::string mutant_data;
@@ -546,14 +547,16 @@ class CentipedeAdaptorRunnerCallbacks
546547
}
547548
auto mutant = FuzzTestFuzzerImpl::Input{*std::move(parsed_origin)};
548549
if (runtime_.run_mode() == RunMode::kFuzz &&
549-
input_cmp_tables[origin_index] == nullptr) {
550-
input_cmp_tables[origin_index] =
551-
std::make_unique<TablesOfRecentCompares>(/*compact=*/true);
552-
PopulateMetadata(inputs[origin_index].metadata,
553-
*input_cmp_tables[origin_index]);
550+
!cmp_tables[origin_index].has_value() &&
551+
inputs[origin_index].metadata != nullptr) {
552+
cmp_tables[origin_index].emplace(/*compact=*/true);
553+
PopulateCmpEntries(*inputs[origin_index].metadata,
554+
*cmp_tables[origin_index]);
554555
}
555-
fuzzer_impl_.MutateValue(mutant, prng_,
556-
{input_cmp_tables[origin_index].get()});
556+
fuzzer_impl_.MutateValue(
557+
mutant, prng_,
558+
{cmp_tables[origin_index].has_value() ? &*cmp_tables[origin_index]
559+
: nullptr});
557560
mutant_data =
558561
fuzzer_impl_.params_domain_.SerializeCorpus(mutant.args).ToString();
559562
}
@@ -566,49 +569,12 @@ class CentipedeAdaptorRunnerCallbacks
566569
~CentipedeAdaptorRunnerCallbacks() override { runtime_.UnsetCurrentArgs(); }
567570

568571
private:
569-
template <typename T>
570-
static void InsertCmpEntryIntoIntegerDictionary(
571-
const uint8_t* a, const uint8_t* b, TablesOfRecentCompares& cmp_tables) {
572-
T a_int;
573-
T b_int;
574-
memcpy(&a_int, a, sizeof(T));
575-
memcpy(&b_int, b, sizeof(T));
576-
cmp_tables.GetMutable<sizeof(T)>().Insert(a_int, b_int);
577-
}
578-
579-
static void PopulateMetadata(
580-
const fuzztest::internal::ExecutionMetadata* metadata,
581-
TablesOfRecentCompares& cmp_tables) {
582-
if (metadata == nullptr) return;
583-
metadata->ForEachCmpEntry([&cmp_tables](fuzztest::internal::ByteSpan a,
584-
fuzztest::internal::ByteSpan b) {
585-
FUZZTEST_CHECK(a.size() == b.size())
586-
<< "cmp operands must have the same size";
587-
const size_t size = a.size();
588-
if (size < kMinCmpEntrySize) return;
589-
if (size > kMaxCmpEntrySize) return;
590-
if (size == 2) {
591-
InsertCmpEntryIntoIntegerDictionary<uint16_t>(a.data(), b.data(),
592-
cmp_tables);
593-
} else if (size == 4) {
594-
InsertCmpEntryIntoIntegerDictionary<uint32_t>(a.data(), b.data(),
595-
cmp_tables);
596-
} else if (size == 8) {
597-
InsertCmpEntryIntoIntegerDictionary<uint64_t>(a.data(), b.data(),
598-
cmp_tables);
599-
}
600-
cmp_tables.GetMutable<0>().Insert(a.data(), b.data(), size);
601-
});
602-
}
603-
604-
// Size limits on the cmp entries to be used in mutation.
605-
static constexpr uint8_t kMaxCmpEntrySize = 15;
606-
static constexpr uint8_t kMinCmpEntrySize = 2;
607-
608572
Runtime& runtime_;
609573
FuzzTestFuzzerImpl& fuzzer_impl_;
610574
const Configuration& configuration_;
611575
absl::BitGen prng_;
576+
std::vector<std::optional<fuzztest::internal::TablesOfRecentCompares>>
577+
cmp_tables;
612578
};
613579

614580
namespace {

0 commit comments

Comments
 (0)