aboutsummaryrefslogtreecommitdiff
path: root/src/mutator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mutator.cc')
-rw-r--r--src/mutator.cc609
1 files changed, 348 insertions, 261 deletions
diff --git a/src/mutator.cc b/src/mutator.cc
index e4e0305..45acd48 100644
--- a/src/mutator.cc
+++ b/src/mutator.cc
@@ -15,9 +15,12 @@
#include "src/mutator.h"
#include <algorithm>
+#include <bitset>
#include <map>
+#include <memory>
#include <random>
#include <string>
+#include <utility>
#include <vector>
#include "src/field_instance.h"
@@ -26,6 +29,7 @@
namespace protobuf_mutator {
+using google::protobuf::Any;
using protobuf::Descriptor;
using protobuf::FieldDescriptor;
using protobuf::FileDescriptor;
@@ -40,17 +44,22 @@ namespace {
const int kMaxInitializeDepth = 200;
const uint64_t kDefaultMutateWeight = 1000000;
-enum class Mutation {
+enum class Mutation : uint8_t {
None,
Add, // Adds new field with default value.
Mutate, // Mutates field contents.
Delete, // Deletes field.
Copy, // Copy values copied from another field.
+ Clone, // Create new field with value copied from another.
- // TODO(vitalybuka):
- // Clone, // Adds new field with value copied from another field.
+ Last = Clone,
};
+using MutationBitset = std::bitset<static_cast<size_t>(Mutation::Last)>;
+
+using Messages = std::vector<Message*>;
+using ConstMessages = std::vector<const Message*>;
+
// Return random integer from [0, count)
size_t GetRandomIndex(RandomEngine* random, size_t count) {
assert(count > 0);
@@ -124,14 +133,14 @@ class CanCopyAndDifferentField
: public FieldFunction<CanCopyAndDifferentField, bool> {
public:
template <class T>
- bool ForType(const ConstFieldInstance& src,
- const ConstFieldInstance& dst) const {
+ bool ForType(const ConstFieldInstance& src, const ConstFieldInstance& dst,
+ int size_increase_hint) const {
T s;
src.Load(&s);
if (!dst.CanStore(s)) return false;
T d;
dst.Load(&d);
- return !IsEqual(s, d);
+ return SizeDiff(s, d) <= size_increase_hint && !IsEqual(s, d);
}
private:
@@ -141,8 +150,8 @@ class CanCopyAndDifferentField
return a.index == b.index;
}
- bool IsEqual(const std::unique_ptr<protobuf::Message>& a,
- const std::unique_ptr<protobuf::Message>& b) const {
+ bool IsEqual(const std::unique_ptr<Message>& a,
+ const std::unique_ptr<Message>& b) const {
return MessageDifferencer::Equals(*a, *b);
}
@@ -150,17 +159,31 @@ class CanCopyAndDifferentField
bool IsEqual(const T& a, const T& b) const {
return a == b;
}
+
+ int64_t SizeDiff(const std::unique_ptr<Message>& src,
+ const std::unique_ptr<Message>& dst) const {
+ return src->ByteSizeLong() - dst->ByteSizeLong();
+ }
+
+ int64_t SizeDiff(const std::string& src, const std::string& dst) const {
+ return src.size() - dst.size();
+ }
+
+ template <class T>
+ int64_t SizeDiff(const T&, const T&) const {
+ return 0;
+ }
};
// Selects random field and mutation from the given proto message.
class MutationSampler {
public:
- MutationSampler(bool keep_initialized, RandomEngine* random, Message* message)
- : keep_initialized_(keep_initialized), random_(random), sampler_(random) {
- Sample(message);
- assert(mutation() != Mutation::None ||
- message->GetDescriptor()->field_count() == 0);
- }
+ MutationSampler(bool keep_initialized, MutationBitset allowed_mutations,
+ RandomEngine* random)
+ : keep_initialized_(keep_initialized),
+ allowed_mutations_(allowed_mutations),
+ random_(random),
+ sampler_(random) {}
// Returns selected field.
const FieldInstance& field() const { return sampler_.selected().field; }
@@ -168,8 +191,15 @@ class MutationSampler {
// Returns selected mutation.
Mutation mutation() const { return sampler_.selected().mutation; }
- private:
void Sample(Message* message) {
+ SampleImpl(message);
+ assert(mutation() != Mutation::None ||
+ !allowed_mutations_[static_cast<size_t>(Mutation::Mutate)] ||
+ message->GetDescriptor()->field_count() == 0);
+ }
+
+ private:
+ void SampleImpl(Message* message) {
const Descriptor* descriptor = message->GetDescriptor();
const Reflection* reflection = message->GetReflection();
@@ -186,59 +216,46 @@ class MutationSampler {
const FieldDescriptor* add_field =
oneof->field(GetRandomIndex(random_, oneof->field_count()));
if (add_field != current_field) {
- sampler_.Try(kDefaultMutateWeight,
- {{message, add_field}, Mutation::Add});
+ Try({message, add_field}, Mutation::Add);
+ Try({message, add_field}, Mutation::Clone);
break;
}
if (oneof->field_count() < 2) break;
}
if (current_field) {
- if (current_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- sampler_.Try(kDefaultMutateWeight,
- {{message, current_field}, Mutation::Mutate});
- }
- sampler_.Try(kDefaultMutateWeight,
- {{message, current_field}, Mutation::Delete});
- sampler_.Try(kDefaultMutateWeight,
- {{message, current_field}, Mutation::Copy});
+ if (current_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE)
+ Try({message, current_field}, Mutation::Mutate);
+ Try({message, current_field}, Mutation::Delete);
+ Try({message, current_field}, Mutation::Copy);
}
}
} else {
if (field->is_repeated()) {
int field_size = reflection->FieldSize(*message, field);
- sampler_.Try(
- kDefaultMutateWeight,
- {{message, field, GetRandomIndex(random_, field_size + 1)},
- Mutation::Add});
+ size_t random_index = GetRandomIndex(random_, field_size + 1);
+ Try({message, field, random_index}, Mutation::Add);
+ Try({message, field, random_index}, Mutation::Clone);
if (field_size) {
size_t random_index = GetRandomIndex(random_, field_size);
- if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- sampler_.Try(kDefaultMutateWeight,
- {{message, field, random_index}, Mutation::Mutate});
- }
- sampler_.Try(kDefaultMutateWeight,
- {{message, field, random_index}, Mutation::Delete});
- sampler_.Try(kDefaultMutateWeight,
- {{message, field, random_index}, Mutation::Copy});
+ if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE)
+ Try({message, field, random_index}, Mutation::Mutate);
+ Try({message, field, random_index}, Mutation::Delete);
+ Try({message, field, random_index}, Mutation::Copy);
}
} else {
if (reflection->HasField(*message, field) ||
IsProto3SimpleField(*field)) {
- if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- sampler_.Try(kDefaultMutateWeight,
- {{message, field}, Mutation::Mutate});
- }
+ if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE)
+ Try({message, field}, Mutation::Mutate);
if (!IsProto3SimpleField(*field) &&
(!field->is_required() || !keep_initialized_)) {
- sampler_.Try(kDefaultMutateWeight,
- {{message, field}, Mutation::Delete});
+ Try({message, field}, Mutation::Delete);
}
- sampler_.Try(kDefaultMutateWeight,
- {{message, field}, Mutation::Copy});
+ Try({message, field}, Mutation::Copy);
} else {
- sampler_.Try(kDefaultMutateWeight,
- {{message, field}, Mutation::Add});
+ Try({message, field}, Mutation::Add);
+ Try({message, field}, Mutation::Clone);
}
}
}
@@ -247,15 +264,22 @@ class MutationSampler {
if (field->is_repeated()) {
const int field_size = reflection->FieldSize(*message, field);
for (int j = 0; j < field_size; ++j)
- Sample(reflection->MutableRepeatedMessage(message, field, j));
+ SampleImpl(reflection->MutableRepeatedMessage(message, field, j));
} else if (reflection->HasField(*message, field)) {
- Sample(reflection->MutableMessage(message, field));
+ SampleImpl(reflection->MutableMessage(message, field));
}
}
}
}
+ void Try(const FieldInstance& field, Mutation mutation) {
+ assert(mutation != Mutation::None);
+ if (!allowed_mutations_[static_cast<size_t>(mutation)]) return;
+ sampler_.Try(kDefaultMutateWeight, {field, mutation});
+ }
+
bool keep_initialized_ = false;
+ MutationBitset allowed_mutations_;
RandomEngine* random_;
@@ -273,10 +297,13 @@ class MutationSampler {
class DataSourceSampler {
public:
DataSourceSampler(const ConstFieldInstance& match, RandomEngine* random,
- Message* message)
- : match_(match), random_(random), sampler_(random) {
- Sample(message);
- }
+ int size_increase_hint)
+ : match_(match),
+ random_(random),
+ size_increase_hint_(size_increase_hint),
+ sampler_(random) {}
+
+ void Sample(const Message& message) { SampleImpl(message); }
// Returns selected field.
const ConstFieldInstance& field() const {
@@ -287,21 +314,21 @@ class DataSourceSampler {
bool IsEmpty() const { return sampler_.IsEmpty(); }
private:
- void Sample(Message* message) {
- const Descriptor* descriptor = message->GetDescriptor();
- const Reflection* reflection = message->GetReflection();
+ void SampleImpl(const Message& message) {
+ const Descriptor* descriptor = message.GetDescriptor();
+ const Reflection* reflection = message.GetReflection();
int field_count = descriptor->field_count();
for (int i = 0; i < field_count; ++i) {
const FieldDescriptor* field = descriptor->field(i);
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
if (field->is_repeated()) {
- const int field_size = reflection->FieldSize(*message, field);
+ const int field_size = reflection->FieldSize(message, field);
for (int j = 0; j < field_size; ++j) {
- Sample(reflection->MutableRepeatedMessage(message, field, j));
+ SampleImpl(reflection->GetRepeatedMessage(message, field, j));
}
- } else if (reflection->HasField(*message, field)) {
- Sample(reflection->MutableMessage(message, field));
+ } else if (reflection->HasField(message, field)) {
+ SampleImpl(reflection->GetMessage(message, field));
}
}
@@ -313,16 +340,16 @@ class DataSourceSampler {
}
if (field->is_repeated()) {
- if (int field_size = reflection->FieldSize(*message, field)) {
- ConstFieldInstance source(message, field,
+ if (int field_size = reflection->FieldSize(message, field)) {
+ ConstFieldInstance source(&message, field,
GetRandomIndex(random_, field_size));
- if (CanCopyAndDifferentField()(source, match_))
+ if (CanCopyAndDifferentField()(source, match_, size_increase_hint_))
sampler_.Try(field_size, source);
}
} else {
- if (reflection->HasField(*message, field)) {
- ConstFieldInstance source(message, field);
- if (CanCopyAndDifferentField()(source, match_))
+ if (reflection->HasField(message, field)) {
+ ConstFieldInstance source(&message, field);
+ if (CanCopyAndDifferentField()(source, match_, size_increase_hint_))
sampler_.Try(1, source);
}
}
@@ -331,19 +358,162 @@ class DataSourceSampler {
ConstFieldInstance match_;
RandomEngine* random_;
+ int size_increase_hint_;
WeightedReservoirSampler<ConstFieldInstance, RandomEngine> sampler_;
};
+using UnpackedAny =
+ std::unordered_map<const Message*, std::unique_ptr<Message>>;
+
+const Descriptor* GetAnyTypeDescriptor(const Any& any) {
+ std::string type_name;
+ if (!Any::ParseAnyTypeUrl(std::string(any.type_url()), &type_name))
+ return nullptr;
+ return any.descriptor()->file()->pool()->FindMessageTypeByName(type_name);
+}
+
+std::unique_ptr<Message> UnpackAny(const Any& any) {
+ const Descriptor* desc = GetAnyTypeDescriptor(any);
+ if (!desc) return {};
+ std::unique_ptr<Message> message(
+ any.GetReflection()->GetMessageFactory()->GetPrototype(desc)->New());
+ message->ParsePartialFromString(std::string(any.value()));
+ return message;
+}
+
+const Any* CastToAny(const Message* message) {
+ return Any::GetDescriptor() == message->GetDescriptor()
+ ? static_cast<const Any*>(message)
+ : nullptr;
+}
+
+Any* CastToAny(Message* message) {
+ return Any::GetDescriptor() == message->GetDescriptor()
+ ? static_cast<Any*>(message)
+ : nullptr;
+}
+
+std::unique_ptr<Message> UnpackIfAny(const Message& message) {
+ if (const Any* any = CastToAny(&message)) return UnpackAny(*any);
+ return {};
+}
+
+void UnpackAny(const Message& message, UnpackedAny* result) {
+ if (std::unique_ptr<Message> any = UnpackIfAny(message)) {
+ UnpackAny(*any, result);
+ result->emplace(&message, std::move(any));
+ return;
+ }
+
+ const Descriptor* descriptor = message.GetDescriptor();
+ const Reflection* reflection = message.GetReflection();
+
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ const FieldDescriptor* field = descriptor->field(i);
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ if (field->is_repeated()) {
+ const int field_size = reflection->FieldSize(message, field);
+ for (int j = 0; j < field_size; ++j) {
+ UnpackAny(reflection->GetRepeatedMessage(message, field, j), result);
+ }
+ } else if (reflection->HasField(message, field)) {
+ UnpackAny(reflection->GetMessage(message, field), result);
+ }
+ }
+ }
+}
+
+class PostProcessing {
+ public:
+ using PostProcessors =
+ std::unordered_multimap<const Descriptor*, Mutator::PostProcess>;
+
+ PostProcessing(bool keep_initialized, const PostProcessors& post_processors,
+ const UnpackedAny& any, RandomEngine* random)
+ : keep_initialized_(keep_initialized),
+ post_processors_(post_processors),
+ any_(any),
+ random_(random) {}
+
+ void Run(Message* message, int max_depth) {
+ --max_depth;
+ const Descriptor* descriptor = message->GetDescriptor();
+
+ // Apply custom mutators in nested messages before packing any.
+ const Reflection* reflection = message->GetReflection();
+ for (int i = 0; i < descriptor->field_count(); i++) {
+ const FieldDescriptor* field = descriptor->field(i);
+ if (keep_initialized_ &&
+ (field->is_required() || descriptor->options().map_entry()) &&
+ !reflection->HasField(*message, field)) {
+ CreateDefaultField()(FieldInstance(message, field));
+ }
+
+ if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) continue;
+
+ if (max_depth < 0 && !field->is_required()) {
+ // Clear deep optional fields to avoid stack overflow.
+ reflection->ClearField(message, field);
+ if (field->is_repeated())
+ assert(!reflection->FieldSize(*message, field));
+ else
+ assert(!reflection->HasField(*message, field));
+ continue;
+ }
+
+ if (field->is_repeated()) {
+ const int field_size = reflection->FieldSize(*message, field);
+ for (int j = 0; j < field_size; ++j) {
+ Message* nested_message =
+ reflection->MutableRepeatedMessage(message, field, j);
+ Run(nested_message, max_depth);
+ }
+ } else if (reflection->HasField(*message, field)) {
+ Message* nested_message = reflection->MutableMessage(message, field);
+ Run(nested_message, max_depth);
+ }
+ }
+
+ if (Any* any = CastToAny(message)) {
+ if (max_depth < 0) {
+ // Clear deep Any fields to avoid stack overflow.
+ any->Clear();
+ } else {
+ auto It = any_.find(message);
+ if (It != any_.end()) {
+ Run(It->second.get(), max_depth);
+ std::string value;
+ It->second->SerializePartialToString(&value);
+ *any->mutable_value() = value;
+ }
+ }
+ }
+
+ // Call user callback after message trimmed, initialized and packed.
+ auto range = post_processors_.equal_range(descriptor);
+ for (auto it = range.first; it != range.second; ++it)
+ it->second(message, (*random_)());
+ }
+
+ private:
+ bool keep_initialized_;
+ const PostProcessors& post_processors_;
+ const UnpackedAny& any_;
+ RandomEngine* random_;
+};
+
} // namespace
class FieldMutator {
public:
- FieldMutator(size_t size_increase_hint, bool enforce_changes,
- bool enforce_utf8_strings, Mutator* mutator)
+ FieldMutator(int size_increase_hint, bool enforce_changes,
+ bool enforce_utf8_strings, const ConstMessages& sources,
+ Mutator* mutator)
: size_increase_hint_(size_increase_hint),
enforce_changes_(enforce_changes),
enforce_utf8_strings_(enforce_utf8_strings),
+ sources_(sources),
mutator_(mutator) {}
void Mutate(int32_t* value) const {
@@ -395,7 +565,8 @@ class FieldMutator {
assert(*message);
if (GetRandomBool(mutator_->random(), mutator_->random_to_default_ratio_))
return;
- mutator_->MutateImpl(message->get(), size_increase_hint_);
+ mutator_->MutateImpl(sources_, {message->get()}, false,
+ size_increase_hint_);
}
private:
@@ -412,9 +583,10 @@ class FieldMutator {
}
}
- size_t size_increase_hint_;
+ int size_increase_hint_;
size_t enforce_changes_;
bool enforce_utf8_strings_;
+ const ConstMessages& sources_;
Mutator* mutator_;
};
@@ -422,11 +594,12 @@ namespace {
struct MutateField : public FieldFunction<MutateField> {
template <class T>
- void ForType(const FieldInstance& field, size_t size_increase_hint,
- Mutator* mutator) const {
+ void ForType(const FieldInstance& field, int size_increase_hint,
+ const ConstMessages& sources, Mutator* mutator) const {
T value;
field.Load(&value);
- FieldMutator(size_increase_hint, true, field.EnforceUtf8(), mutator)
+ FieldMutator(size_increase_hint, true, field.EnforceUtf8(), sources,
+ mutator)
.Mutate(&value);
field.Store(value);
}
@@ -435,13 +608,13 @@ struct MutateField : public FieldFunction<MutateField> {
struct CreateField : public FieldFunction<CreateField> {
public:
template <class T>
- void ForType(const FieldInstance& field, size_t size_increase_hint,
- Mutator* mutator) const {
+ void ForType(const FieldInstance& field, int size_increase_hint,
+ const ConstMessages& sources, Mutator* mutator) const {
T value;
field.GetDefault(&value);
FieldMutator field_mutator(size_increase_hint,
false /* defaults could be useful */,
- field.EnforceUtf8(), mutator);
+ field.EnforceUtf8(), sources, mutator);
field_mutator.Mutate(&value);
field.Create(value);
}
@@ -451,210 +624,116 @@ struct CreateField : public FieldFunction<CreateField> {
void Mutator::Seed(uint32_t value) { random_.seed(value); }
-void Mutator::Mutate(Message* message, size_t size_increase_hint) {
- MutateImpl(message, size_increase_hint);
+void Mutator::Mutate(Message* message, size_t max_size_hint) {
+ UnpackedAny any;
+ UnpackAny(*message, &any);
- InitializeAndTrim(message, kMaxInitializeDepth);
- assert(!keep_initialized_ || message->IsInitialized());
+ Messages messages;
+ messages.reserve(any.size() + 1);
+ messages.push_back(message);
+ for (const auto& kv : any) messages.push_back(kv.second.get());
- if (!post_processors_.empty()) {
- ApplyPostProcessing(message);
- }
-}
+ ConstMessages sources(messages.begin(), messages.end());
+ MutateImpl(sources, messages, false,
+ static_cast<int>(max_size_hint) -
+ static_cast<int>(message->ByteSizeLong()));
-void Mutator::RegisterPostProcessor(const protobuf::Descriptor* desc,
- PostProcess callback) {
- post_processors_.emplace(desc, callback);
+ PostProcessing(keep_initialized_, post_processors_, any, &random_)
+ .Run(message, kMaxInitializeDepth);
+ assert(IsInitialized(*message));
}
-void Mutator::ApplyPostProcessing(Message* message) {
- const Descriptor* descriptor = message->GetDescriptor();
+void Mutator::CrossOver(const Message& message1, Message* message2,
+ size_t max_size_hint) {
+ UnpackedAny any;
+ UnpackAny(*message2, &any);
- auto it = post_processors_.find(descriptor);
- if (it != post_processors_.end()) {
- it->second(message, random_());
- }
+ Messages messages;
+ messages.reserve(any.size() + 1);
+ messages.push_back(message2);
+ for (auto& kv : any) messages.push_back(kv.second.get());
- // Now recursively apply custom mutators.
- const Reflection* reflection = message->GetReflection();
- for (int i = 0; i < descriptor->field_count(); i++) {
- const FieldDescriptor* field = descriptor->field(i);
- if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- continue;
- }
- if (field->is_repeated()) {
- const int field_size = reflection->FieldSize(*message, field);
- for (int j = 0; j < field_size; ++j) {
- Message* nested_message =
- reflection->MutableRepeatedMessage(message, field, j);
- ApplyPostProcessing(nested_message);
- }
- } else if (reflection->HasField(*message, field)) {
- Message* nested_message = reflection->MutableMessage(message, field);
- ApplyPostProcessing(nested_message);
- }
- }
+ UnpackAny(message1, &any);
+
+ ConstMessages sources;
+ sources.reserve(any.size() + 2);
+ sources.push_back(&message1);
+ sources.push_back(message2);
+ for (const auto& kv : any) sources.push_back(kv.second.get());
+
+ MutateImpl(sources, messages, true,
+ static_cast<int>(max_size_hint) -
+ static_cast<int>(message2->ByteSizeLong()));
+
+ PostProcessing(keep_initialized_, post_processors_, any, &random_)
+ .Run(message2, kMaxInitializeDepth);
+ assert(IsInitialized(*message2));
+}
+
+void Mutator::RegisterPostProcessor(const Descriptor* desc,
+ PostProcess callback) {
+ post_processors_.emplace(desc, callback);
}
-void Mutator::MutateImpl(Message* message, size_t size_increase_hint) {
- for (;;) {
- MutationSampler mutation(keep_initialized_, &random_, message);
+bool Mutator::MutateImpl(const ConstMessages& sources, const Messages& messages,
+ bool copy_clone_only, int size_increase_hint) {
+ MutationBitset mutations;
+ if (copy_clone_only) {
+ mutations[static_cast<size_t>(Mutation::Copy)] = true;
+ mutations[static_cast<size_t>(Mutation::Clone)] = true;
+ } else if (size_increase_hint <= 16) {
+ mutations[static_cast<size_t>(Mutation::Delete)] = true;
+ } else {
+ mutations.set();
+ mutations[static_cast<size_t>(Mutation::Copy)] = false;
+ mutations[static_cast<size_t>(Mutation::Clone)] = false;
+ }
+ while (mutations.any()) {
+ MutationSampler mutation(keep_initialized_, mutations, &random_);
+ for (Message* message : messages) mutation.Sample(message);
+
switch (mutation.mutation()) {
case Mutation::None:
- return;
+ return true;
case Mutation::Add:
- CreateField()(mutation.field(), size_increase_hint / 2, this);
- return;
+ CreateField()(mutation.field(), size_increase_hint, sources, this);
+ return true;
case Mutation::Mutate:
- MutateField()(mutation.field(), size_increase_hint / 2, this);
- return;
+ MutateField()(mutation.field(), size_increase_hint, sources, this);
+ return true;
case Mutation::Delete:
DeleteField()(mutation.field());
- return;
+ return true;
+ case Mutation::Clone: {
+ CreateDefaultField()(mutation.field());
+ DataSourceSampler source_sampler(mutation.field(), &random_,
+ size_increase_hint);
+ for (const Message* source : sources) source_sampler.Sample(*source);
+ if (source_sampler.IsEmpty()) {
+ if (!IsProto3SimpleField(*mutation.field().descriptor()))
+ return true; // CreateField is enough for proto2.
+ break;
+ }
+ CopyField()(source_sampler.field(), mutation.field());
+ return true;
+ }
case Mutation::Copy: {
- DataSourceSampler source(mutation.field(), &random_, message);
- if (source.IsEmpty()) break;
- CopyField()(source.field(), mutation.field());
- return;
+ DataSourceSampler source_sampler(mutation.field(), &random_,
+ size_increase_hint);
+ for (const Message* source : sources) source_sampler.Sample(*source);
+ if (source_sampler.IsEmpty()) break;
+ CopyField()(source_sampler.field(), mutation.field());
+ return true;
}
default:
assert(false && "unexpected mutation");
- return;
+ return false;
}
- }
-}
-void Mutator::CrossOver(const protobuf::Message& message1,
- protobuf::Message* message2) {
- // CrossOver can produce result which still equals to inputs. So we backup
- // message2 to later comparison. message1 is already constant.
- std::unique_ptr<protobuf::Message> message2_copy(message2->New());
- message2_copy->CopyFrom(*message2);
-
- CrossOverImpl(message1, message2);
-
- InitializeAndTrim(message2, kMaxInitializeDepth);
- assert(!keep_initialized_ || message2->IsInitialized());
-
- if (!post_processors_.empty()) {
- ApplyPostProcessing(message2);
- }
-
- // Can't call mutate from crossover because of a bug in libFuzzer.
- // if (MessageDifferencer::Equals(*message2_copy, *message2) ||
- // MessageDifferencer::Equals(message1, *message2)) {
- // Mutate(message2, 0);
- // }
-}
-
-void Mutator::CrossOverImpl(const protobuf::Message& message1,
- protobuf::Message* message2) {
- const Descriptor* descriptor = message2->GetDescriptor();
- const Reflection* reflection = message2->GetReflection();
- assert(message1.GetDescriptor() == descriptor);
- assert(message1.GetReflection() == reflection);
-
- for (int i = 0; i < descriptor->field_count(); ++i) {
- const FieldDescriptor* field = descriptor->field(i);
-
- if (field->is_repeated()) {
- const int field_size1 = reflection->FieldSize(message1, field);
- int field_size2 = reflection->FieldSize(*message2, field);
- for (int j = 0; j < field_size1; ++j) {
- ConstFieldInstance source(&message1, field, j);
- FieldInstance destination(message2, field, field_size2++);
- AppendField()(source, destination);
- }
-
- assert(field_size2 == reflection->FieldSize(*message2, field));
-
- // Shuffle
- for (int j = 0; j < field_size2; ++j) {
- if (int k = GetRandomIndex(&random_, field_size2 - j)) {
- reflection->SwapElements(message2, field, j, j + k);
- }
- }
-
- int keep = GetRandomIndex(&random_, field_size2 + 1);
-
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- int remove = field_size2 - keep;
- // Cross some message to keep with messages to remove.
- int cross = GetRandomIndex(&random_, std::min(keep, remove) + 1);
- for (int j = 0; j < cross; ++j) {
- int k = GetRandomIndex(&random_, keep);
- int r = keep + GetRandomIndex(&random_, remove);
- assert(k != r);
- CrossOverImpl(reflection->GetRepeatedMessage(*message2, field, r),
- reflection->MutableRepeatedMessage(message2, field, k));
- }
- }
-
- for (int j = keep; j < field_size2; ++j)
- reflection->RemoveLast(message2, field);
- assert(keep == reflection->FieldSize(*message2, field));
-
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- if (!reflection->HasField(message1, field)) {
- if (GetRandomBool(&random_))
- DeleteField()(FieldInstance(message2, field));
- } else if (!reflection->HasField(*message2, field)) {
- if (GetRandomBool(&random_)) {
- ConstFieldInstance source(&message1, field);
- CopyField()(source, FieldInstance(message2, field));
- }
- } else {
- CrossOverImpl(reflection->GetMessage(message1, field),
- reflection->MutableMessage(message2, field));
- }
- } else {
- if (GetRandomBool(&random_)) {
- if (reflection->HasField(message1, field)) {
- ConstFieldInstance source(&message1, field);
- CopyField()(source, FieldInstance(message2, field));
- } else {
- DeleteField()(FieldInstance(message2, field));
- }
- }
- }
- }
-}
-
-void Mutator::InitializeAndTrim(Message* message, int max_depth) {
- const Descriptor* descriptor = message->GetDescriptor();
- const Reflection* reflection = message->GetReflection();
- for (int i = 0; i < descriptor->field_count(); ++i) {
- const FieldDescriptor* field = descriptor->field(i);
- if (keep_initialized_ &&
- (field->is_required() || descriptor->options().map_entry()) &&
- !reflection->HasField(*message, field)) {
- CreateDefaultField()(FieldInstance(message, field));
- }
-
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- if (max_depth <= 0 && !field->is_required()) {
- // Clear deep optional fields to avoid stack overflow.
- reflection->ClearField(message, field);
- if (field->is_repeated())
- assert(!reflection->FieldSize(*message, field));
- else
- assert(!reflection->HasField(*message, field));
- continue;
- }
-
- if (field->is_repeated()) {
- const int field_size = reflection->FieldSize(*message, field);
- for (int j = 0; j < field_size; ++j) {
- Message* nested_message =
- reflection->MutableRepeatedMessage(message, field, j);
- InitializeAndTrim(nested_message, max_depth - 1);
- }
- } else if (reflection->HasField(*message, field)) {
- Message* nested_message = reflection->MutableMessage(message, field);
- InitializeAndTrim(nested_message, max_depth - 1);
- }
- }
+ // Don't try same mutation next time.
+ mutations[static_cast<size_t>(mutation.mutation())] = false;
}
+ return false;
}
int32_t Mutator::MutateInt32(int32_t value) { return FlipBit(value, &random_); }
@@ -681,14 +760,16 @@ size_t Mutator::MutateEnum(size_t index, size_t item_count) {
}
std::string Mutator::MutateString(const std::string& value,
- size_t size_increase_hint) {
+ int size_increase_hint) {
std::string result = value;
while (!result.empty() && GetRandomBool(&random_)) {
result.erase(GetRandomIndex(&random_, result.size()), 1);
}
- while (result.size() < size_increase_hint && GetRandomBool(&random_)) {
+ while (size_increase_hint > 0 &&
+ result.size() < static_cast<size_t>(size_increase_hint) &&
+ GetRandomBool(&random_)) {
size_t index = GetRandomIndex(&random_, result.size() + 1);
result.insert(result.begin() + index, GetRandomIndex(&random_, 1 << 8));
}
@@ -706,10 +787,16 @@ std::string Mutator::MutateString(const std::string& value,
}
std::string Mutator::MutateUtf8String(const std::string& value,
- size_t size_increase_hint) {
+ int size_increase_hint) {
std::string str = MutateString(value, size_increase_hint);
FixUtf8String(&str, &random_);
return str;
}
+bool Mutator::IsInitialized(const Message& message) const {
+ if (!keep_initialized_ || message.IsInitialized()) return true;
+ std::cerr << "Uninitialized: " << message.DebugString() << "\n";
+ return false;
+}
+
} // namespace protobuf_mutator