diff options
| author | Vitaly Buka <vitalybuka@google.com> | 2020-07-28 23:41:42 -0700 |
|---|---|---|
| committer | Vitaly Buka <vitalybuka@gmail.com> | 2020-07-29 20:01:18 -0700 |
| commit | d82b5fb6e6fd87ca3a97d35cfcfb0b1da5a07709 (patch) | |
| tree | 721eae771eecdbf102de633ca3ebe872c6980740 | |
| parent | 7a2ed51a6b682a83e345ff49fc4cfd7ca47550db (diff) | |
| download | platform_external_libprotobuf-mutator-d82b5fb6e6fd87ca3a97d35cfcfb0b1da5a07709.tar.gz platform_external_libprotobuf-mutator-d82b5fb6e6fd87ca3a97d35cfcfb0b1da5a07709.tar.bz2 platform_external_libprotobuf-mutator-d82b5fb6e6fd87ca3a97d35cfcfb0b1da5a07709.zip | |
Fix message before every LLVMFuzzerTestOneInput
This is needed to make sure that corpus is still compartible.
Fixed #177
| -rw-r--r-- | README.md | 15 | ||||
| -rw-r--r-- | examples/libfuzzer/libfuzzer_bin_example.cc | 46 | ||||
| -rw-r--r-- | examples/libfuzzer/libfuzzer_example.cc | 46 | ||||
| -rw-r--r-- | src/libfuzzer/libfuzzer_macro.cc | 8 | ||||
| -rw-r--r-- | src/libfuzzer/libfuzzer_test.cc | 22 | ||||
| -rw-r--r-- | src/mutator.cc | 9 | ||||
| -rw-r--r-- | src/mutator.h | 3 |
7 files changed, 90 insertions, 59 deletions
@@ -98,18 +98,23 @@ PostProcessorRegistration can be used to avoid such issue and guide your fuzzer 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) { - TweakMyMessage(message, seed); - }}; +static protobuf_mutator::libfuzzer::PostProcessorRegistration<MyMessageType> reg = { + [](MyMessageType* message, unsigned int seed) { + TweakMyMessage(message, seed); + }}; +DEFINE_PROTO_FUZZER(const MyMessageType& input) { // Code which needs to be fuzzed. ConsumeMyMessageType(input); } ``` Optional: Use seed if callback uses random numbers. It may help later with debugging. +Important: Callbacks should be deterministic and avoid modifying good messages. +Callbacks are called for both: mutator generated and user provided inputs, like +corpus or bug reproducer. So if callback performs unnecessary transformation it +may corrupt the reproducer so it stops triggering the bug. + Note: You can add callback for any nested message and you can add multiple callbacks for the same message type. ``` diff --git a/examples/libfuzzer/libfuzzer_bin_example.cc b/examples/libfuzzer/libfuzzer_bin_example.cc index e5665c4..963b522 100644 --- a/examples/libfuzzer/libfuzzer_bin_example.cc +++ b/examples/libfuzzer/libfuzzer_bin_example.cc @@ -21,32 +21,30 @@ protobuf_mutator::protobuf::LogSilencer log_silincer; -DEFINE_BINARY_PROTO_FUZZER(const libfuzzer_example::Msg& message) { - static PostProcessorRegistration reg = { - [](libfuzzer_example::Msg* message, unsigned int seed) { - if (seed % 2) { - message->set_optional_uint64( - std::hash<std::string>{}(message->optional_string())); - } - - if (message->has_any()) { - auto* any = message->mutable_any(); - - // Guide mutator to usefull 'Any' types. - static const char* const expected_types[] = { - "type.googleapis.com/google.protobuf.DescriptorProto", - "type.googleapis.com/google.protobuf.FileDescriptorProto", - }; - - if (!std::count(std::begin(expected_types), std::end(expected_types), - any->type_url())) { - const size_t num = - (std::end(expected_types) - std::begin(expected_types)); - any->set_type_url(expected_types[seed % num]); - } +protobuf_mutator::libfuzzer::PostProcessorRegistration<libfuzzer_example::Msg> + reg = {[](libfuzzer_example::Msg* message, unsigned int seed) { + message->set_optional_uint64( + std::hash<std::string>{}(message->optional_string())); + + if (message->has_any()) { + auto* any = message->mutable_any(); + + // Guide mutator to usefull 'Any' types. + static const char* const expected_types[] = { + "type.googleapis.com/google.protobuf.DescriptorProto", + "type.googleapis.com/google.protobuf.FileDescriptorProto", + }; + + if (!std::count(std::begin(expected_types), std::end(expected_types), + any->type_url())) { + const size_t num = + (std::end(expected_types) - std::begin(expected_types)); + any->set_type_url(expected_types[seed % num]); } - }}; + } + }}; +DEFINE_BINARY_PROTO_FUZZER(const libfuzzer_example::Msg& message) { protobuf_mutator::protobuf::FileDescriptorProto file; // Emulate a bug. diff --git a/examples/libfuzzer/libfuzzer_example.cc b/examples/libfuzzer/libfuzzer_example.cc index 2f2b8d4..aa65125 100644 --- a/examples/libfuzzer/libfuzzer_example.cc +++ b/examples/libfuzzer/libfuzzer_example.cc @@ -21,32 +21,30 @@ protobuf_mutator::protobuf::LogSilencer log_silincer; -DEFINE_PROTO_FUZZER(const libfuzzer_example::Msg& message) { - static PostProcessorRegistration reg = { - [](libfuzzer_example::Msg* message, unsigned int seed) { - if (seed % 2) { - message->set_optional_uint64( - std::hash<std::string>{}(message->optional_string())); - } - - if (message->has_any()) { - auto* any = message->mutable_any(); - - // Guide mutator to usefull 'Any' types. - static const char* const expected_types[] = { - "type.googleapis.com/google.protobuf.DescriptorProto", - "type.googleapis.com/google.protobuf.FileDescriptorProto", - }; - - if (!std::count(std::begin(expected_types), std::end(expected_types), - any->type_url())) { - const size_t num = - (std::end(expected_types) - std::begin(expected_types)); - any->set_type_url(expected_types[seed % num]); - } +protobuf_mutator::libfuzzer::PostProcessorRegistration<libfuzzer_example::Msg> + reg = {[](libfuzzer_example::Msg* message, unsigned int seed) { + message->set_optional_uint64( + std::hash<std::string>{}(message->optional_string())); + + if (message->has_any()) { + auto* any = message->mutable_any(); + + // Guide mutator to usefull 'Any' types. + static const char* const expected_types[] = { + "type.googleapis.com/google.protobuf.DescriptorProto", + "type.googleapis.com/google.protobuf.FileDescriptorProto", + }; + + if (!std::count(std::begin(expected_types), std::end(expected_types), + any->type_url())) { + const size_t num = + (std::end(expected_types) - std::begin(expected_types)); + any->set_type_url(expected_types[seed % num]); } - }}; + } + }}; +DEFINE_PROTO_FUZZER(const libfuzzer_example::Msg& message) { protobuf_mutator::protobuf::FileDescriptorProto file; // Emulate a bug. diff --git a/src/libfuzzer/libfuzzer_macro.cc b/src/libfuzzer/libfuzzer_macro.cc index b2a5302..11730cf 100644 --- a/src/libfuzzer/libfuzzer_macro.cc +++ b/src/libfuzzer/libfuzzer_macro.cc @@ -189,8 +189,12 @@ 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) { - return binary ? ParseBinaryMessage(data, size, input) - : ParseTextMessage(data, size, input); + auto result = binary ? ParseBinaryMessage(data, size, input) + : ParseTextMessage(data, size, input); + if (!result) return false; + GetMutator()->Seed(size); + GetMutator()->Fix(input); + return true; } void RegisterPostProcessor( diff --git a/src/libfuzzer/libfuzzer_test.cc b/src/libfuzzer/libfuzzer_test.cc index 180a02c..e0826c9 100644 --- a/src/libfuzzer/libfuzzer_test.cc +++ b/src/libfuzzer/libfuzzer_test.cc @@ -17,12 +17,26 @@ #include "src/mutator_test_proto2.pb.h" static bool reached = false; +static bool postprocessed = false; -DEFINE_PROTO_FUZZER(const protobuf_mutator::Msg::EmptyMessage& message) { +protobuf_mutator::libfuzzer::PostProcessorRegistration<protobuf_mutator::Msg> + reg = {[](protobuf_mutator::Msg* message, unsigned int seed) { + static unsigned int first_seed = seed; + EXPECT_EQ(seed, first_seed); + postprocessed = true; + }}; + +DEFINE_TEXT_PROTO_FUZZER(const protobuf_mutator::Msg& message) { reached = true; + EXPECT_TRUE(message.IsInitialized()); + EXPECT_TRUE(postprocessed); } -TEST(LibFuzzerTest, Basic) { - LLVMFuzzerTestOneInput((const uint8_t*)"", 0); - EXPECT_TRUE(reached); +TEST(LibFuzzerTest, LLVMFuzzerTestOneInput) { + for (int i = 0; i < 10; ++i) { + reached = false; + postprocessed = false; + LLVMFuzzerTestOneInput((const uint8_t*)"", 0); + EXPECT_TRUE(reached); + } } diff --git a/src/mutator.cc b/src/mutator.cc index b29ea8f..fb5542b 100644 --- a/src/mutator.cc +++ b/src/mutator.cc @@ -625,6 +625,15 @@ struct CreateField : public FieldFunction<CreateField> { void Mutator::Seed(uint32_t value) { random_.seed(value); } +void Mutator::Fix(Message* message) { + UnpackedAny any; + UnpackAny(*message, &any); + + PostProcessing(keep_initialized_, post_processors_, any, &random_) + .Run(message, kMaxInitializeDepth); + assert(IsInitialized(*message)); +} + void Mutator::Mutate(Message* message, size_t max_size_hint) { UnpackedAny any; UnpackAny(*message, &any); diff --git a/src/mutator.h b/src/mutator.h index 57c7c47..afb6940 100644 --- a/src/mutator.h +++ b/src/mutator.h @@ -60,6 +60,9 @@ class Mutator { void CrossOver(const protobuf::Message& message1, protobuf::Message* message2, size_t max_size_hint); + // Makes message initialized and calls post processors to make it valid. + void Fix(protobuf::Message* message); + // Callback to postprocess mutations. // Implementation should use seed to initialize random number generators. using PostProcess = |
