diff options
author | Haibo Huang <hhb@google.com> | 2020-01-08 15:06:08 -0800 |
---|---|---|
committer | Haibo Huang <hhb@google.com> | 2020-01-08 15:06:08 -0800 |
commit | 7eae75fbc6bbf8803e915829a9c904f63968bf96 (patch) | |
tree | 8acbee52ff99335d6ea02a93ae661067b6bfeaef | |
parent | 59b168c66e67609fd2e5af390f4eba8e73ac6943 (diff) | |
parent | 69d93082200bb657f6db9c6b07b71bb94ea7bb47 (diff) | |
download | libprotobuf-mutator-7eae75fbc6bbf8803e915829a9c904f63968bf96.tar.gz |
Upgrade libprotobuf-mutator to 69d93082200bb657f6db9c6b07b71bb94ea7bb47android-r-preview-1
Test: None
Change-Id: I70e85bc40665b0bf6eaeb692cc0d47be506f65f7
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | README.md | 23 | ||||
-rw-r--r-- | src/libfuzzer/libfuzzer_macro.cc | 5 | ||||
-rw-r--r-- | src/libfuzzer/libfuzzer_macro.h | 9 | ||||
-rw-r--r-- | src/mutator.cc | 47 | ||||
-rw-r--r-- | src/mutator.h | 9 | ||||
-rw-r--r-- | src/mutator_test.cc | 3 |
7 files changed, 82 insertions, 22 deletions
@@ -5,11 +5,11 @@ third_party { type: GIT value: "https://github.com/google/libprotobuf-mutator" } - version: "62c5c91a2bfd675fa99cf61f6747fd227da8983a" + version: "69d93082200bb657f6db9c6b07b71bb94ea7bb47" license_type: NOTICE last_upgrade_date { - year: 2019 - month: 9 - day: 28 + year: 2020 + month: 1 + day: 8 } } @@ -88,6 +88,29 @@ DEFINE_PROTO_FUZZER(const MyMessageType& input) { Please see [libfuzzer_example.cc](/examples/libfuzzer/libfuzzer_example.cc) as an example. +### Mutation post-processing (experimental) +Sometimes it's necessary to keep particular values in some fields without which the proto +is going to be rejected by fuzzed code. E.g. code may expect consistency between some fields +or it may use some fields as checksums. Such constraints are going to be significant bottleneck +for fuzzer even if it's capable of inserting acceptable values with time. + +PostProcessorRegistration can be used to avoid such issue and guide your fuzzer towards interesing +code. It registers callback which will be called for each message of particular type after each mutation. + +``` +DEFINE_PROTO_FUZZER(const MyMessageType& input) { + static PostProcessorRegistration reg = { + [](MyMessageType* message, unsigned int seed) { + TweakMyMessageType(message, seed); + }}; + + // Code which needs to be fuzzed. + ConsumeMyMessageType(input); +} +``` + +Optional: Use seed if callback uses random numbers. It may help later with debugging. + ## UTF-8 strings "proto2" and "proto3" handle invalid UTF-8 strings differently. In both cases string should be UTF-8, however only "proto3" enforces that. So if fuzzer is diff --git a/src/libfuzzer/libfuzzer_macro.cc b/src/libfuzzer/libfuzzer_macro.cc index 5f9023f..c37276d 100644 --- a/src/libfuzzer/libfuzzer_macro.cc +++ b/src/libfuzzer/libfuzzer_macro.cc @@ -184,10 +184,11 @@ bool LoadProtoInput(bool binary, const uint8_t* data, size_t size, : ParseTextMessage(data, size, input); } -void RegisterPostProcessorImpl( +void RegisterPostProcessor( + const protobuf::Descriptor* desc, std::function<void(protobuf::Message* message, unsigned int seed)> callback) { - GetMutator()->RegisterPostProcessor(callback); + GetMutator()->RegisterPostProcessor(desc, callback); } } // namespace libfuzzer diff --git a/src/libfuzzer/libfuzzer_macro.h b/src/libfuzzer/libfuzzer_macro.h index a7d26ab..1a1fe0a 100644 --- a/src/libfuzzer/libfuzzer_macro.h +++ b/src/libfuzzer/libfuzzer_macro.h @@ -106,7 +106,8 @@ size_t CustomProtoCrossOver(bool binary, const uint8_t* data1, size_t size1, bool LoadProtoInput(bool binary, const uint8_t* data, size_t size, protobuf::Message* input); -void RegisterPostProcessorImpl( +void RegisterPostProcessor( + const protobuf::Descriptor* desc, std::function<void(protobuf::Message* message, unsigned int seed)> callback); @@ -114,9 +115,9 @@ template <class Proto> struct PostProcessorRegistration { PostProcessorRegistration( const std::function<void(Proto* message, unsigned int seed)>& callback) { - protobuf_mutator::libfuzzer::RegisterPostProcessorImpl( - [callback](protobuf_mutator::protobuf::Message* message, - unsigned int seed) { + RegisterPostProcessor( + Proto::descriptor(), + [callback](protobuf::Message* message, unsigned int seed) { callback(static_cast<Proto*>(message), seed); }); } diff --git a/src/mutator.cc b/src/mutator.cc index e3c9b39..e4e0305 100644 --- a/src/mutator.cc +++ b/src/mutator.cc @@ -457,7 +457,43 @@ void Mutator::Mutate(Message* message, size_t size_increase_hint) { InitializeAndTrim(message, kMaxInitializeDepth); assert(!keep_initialized_ || message->IsInitialized()); - if (post_process_) post_process_(message, random_()); + if (!post_processors_.empty()) { + ApplyPostProcessing(message); + } +} + +void Mutator::RegisterPostProcessor(const protobuf::Descriptor* desc, + PostProcess callback) { + post_processors_.emplace(desc, callback); +} + +void Mutator::ApplyPostProcessing(Message* message) { + const Descriptor* descriptor = message->GetDescriptor(); + + auto it = post_processors_.find(descriptor); + if (it != post_processors_.end()) { + it->second(message, random_()); + } + + // 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); + } + } } void Mutator::MutateImpl(Message* message, size_t size_increase_hint) { @@ -500,7 +536,9 @@ void Mutator::CrossOver(const protobuf::Message& message1, InitializeAndTrim(message2, kMaxInitializeDepth); assert(!keep_initialized_ || message2->IsInitialized()); - if (post_process_) post_process_(message2, random_()); + if (!post_processors_.empty()) { + ApplyPostProcessing(message2); + } // Can't call mutate from crossover because of a bug in libFuzzer. // if (MessageDifferencer::Equals(*message2_copy, *message2) || @@ -582,11 +620,6 @@ void Mutator::CrossOverImpl(const protobuf::Message& message1, } } -void Mutator::RegisterPostProcessor(PostProcess post_process) { - assert(!post_process_); - post_process_ = post_process; -} - void Mutator::InitializeAndTrim(Message* message, int max_depth) { const Descriptor* descriptor = message->GetDescriptor(); const Reflection* reflection = message->GetReflection(); diff --git a/src/mutator.h b/src/mutator.h index 73e1966..7af7b38 100644 --- a/src/mutator.h +++ b/src/mutator.h @@ -70,7 +70,8 @@ class Mutator { // Register callback which will be called after every message mutation. // In this callback fuzzer may adjust content of the message or mutate some // fields in some fuzzer specific way. - void RegisterPostProcessor(PostProcess post_process); + void RegisterPostProcessor(const protobuf::Descriptor* desc, + PostProcess callback); protected: // TODO(vitalybuka): Consider to replace with single mutate (uint8_t*, size). @@ -85,6 +86,8 @@ class Mutator { virtual std::string MutateString(const std::string& value, size_t size_increase_hint); + std::unordered_map<const protobuf::Descriptor*, PostProcess> post_processors_; + RandomEngine* random() { return &random_; } private: @@ -96,12 +99,10 @@ class Mutator { protobuf::Message* message2); std::string MutateUtf8String(const std::string& value, size_t size_increase_hint); - bool ApplyCustomMutations(protobuf::Message* message, - const protobuf::FieldDescriptor* field); + void ApplyPostProcessing(protobuf::Message* message); bool keep_initialized_ = true; size_t random_to_default_ratio_ = 100; RandomEngine random_; - PostProcess post_process_; }; } // namespace protobuf_mutator diff --git a/src/mutator_test.cc b/src/mutator_test.cc index 0ae37aa..a86760a 100644 --- a/src/mutator_test.cc +++ b/src/mutator_test.cc @@ -593,7 +593,8 @@ TYPED_TEST(MutatorTypedTest, RegisterPostProcessor) { TestMutator mutator(false); mutator.RegisterPostProcessor( - [kIndicatorString](protobuf::Message* message, uint32_t seed) { + TestFixture::Message::descriptor(), + [kIndicatorString](protobuf::Message* message, unsigned int seed) { typename TestFixture::Message* test_message = static_cast<typename TestFixture::Message*>(message); if (seed % 2) test_message->set_optional_string(kIndicatorString); |