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
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 {
3942using 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+
4484struct FuzzTestMutator ::MutationMetadata {
45- fuzztest::internal::TablesOfRecentCompares cmp_tables;
85+ std::vector<std::optional<fuzztest::internal::TablesOfRecentCompares>>
86+ cmp_tables;
4687};
4788
4889class FuzzTestMutator ::MutatorDomain : public MutatorDomainBase {
@@ -101,42 +142,36 @@ void FuzzTestMutator::CrossOver(ByteArray &data, const ByteArray &other) {
101142std::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-
140175bool FuzzTestMutator::set_max_len (size_t max_len) {
141176 max_len_ = max_len;
142177 domain_->WithMaxSize (max_len);
0 commit comments