Skip to content

Commit

Permalink
Support multiple callbacks per message type
Browse files Browse the repository at this point in the history
Fixed #146
  • Loading branch information
vitalybuka committed Jan 10, 2020
1 parent 69d9308 commit ad520b3
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 27 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,23 +94,43 @@ is going to be rejected by fuzzed code. E.g. code may expect consistency between
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
PostProcessorRegistration can be used to avoid such issue and guide your fuzzer towards interesting
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);
TweakMyMessage(message, seed);
}};
// Code which needs to be fuzzed.
ConsumeMyMessageType(input);
}
```

Optional: Use seed if callback uses random numbers. It may help later with debugging.

Note: You can add callback for any nested message and you can add multiple callbacks for
the same message type.
```
DEFINE_PROTO_FUZZER(const MyMessageType& input) {
static PostProcessorRegistration reg1 = {
[](MyMessageType* message, unsigned int seed) {
TweakMyMessage(message, seed);
}};
static PostProcessorRegistration reg2 = {
[](MyMessageType* message, unsigned int seed) {
DifferentTweakMyMessage(message, seed);
}};
static PostProcessorRegistration reg_nested = {
[](MyMessageType::Nested* message, unsigned int seed) {
TweakMyNestedMessage(message, seed);
}};
// Code which needs to be fuzzed.
ConsumeMyMessageType(input);
}
```
## 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
Expand Down
5 changes: 2 additions & 3 deletions src/mutator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,9 @@ void Mutator::RegisterPostProcessor(const protobuf::Descriptor* desc,
void Mutator::ApplyPostProcessing(Message* message) {
const Descriptor* descriptor = message->GetDescriptor();

auto it = post_processors_.find(descriptor);
if (it != post_processors_.end()) {
auto range = post_processors_.equal_range(descriptor);
for (auto it = range.first; it != range.second; ++it)
it->second(message, random_());
}

// Now recursively apply custom mutators.
const Reflection* reflection = message->GetReflection();
Expand Down
3 changes: 2 additions & 1 deletion src/mutator.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +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_;
std::unordered_multimap<const protobuf::Descriptor*, PostProcess>
post_processors_;

RandomEngine* random() { return &random_; }

Expand Down
52 changes: 32 additions & 20 deletions src/mutator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -586,36 +586,48 @@ TYPED_TEST(MutatorTypedTest, FailedMutations) {
}

TYPED_TEST(MutatorTypedTest, RegisterPostProcessor) {
constexpr char kInitialString[] = " ";
constexpr char kIndicatorString[] = "0123456789abcdef";
bool custom_mutation = false;
bool regular_mutation = false;

std::set<std::string> top_mutations = {"0123456789abcdef",
"abcdef0123456789"};
TestMutator mutator(false);
mutator.RegisterPostProcessor(
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);
});
for (auto& v : top_mutations) {
mutator.RegisterPostProcessor(
TestFixture::Message::descriptor(),
[=](protobuf::Message* message, unsigned int seed) {
auto test_message =
static_cast<typename TestFixture::Message*>(message);
if (seed % 2) test_message->set_optional_string(v);
});
}

std::set<int64_t> nested_mutations = {1234567, 567890};
for (auto& v : nested_mutations) {
mutator.RegisterPostProcessor(
TestFixture::Message::SubMsg::descriptor(),
[=](protobuf::Message* message, unsigned int seed) {
auto test_message =
static_cast<typename TestFixture::Message::SubMsg*>(message);
if (seed % 2) test_message->set_optional_int64(v);
});
}

bool regular_mutation = false;

for (int j = 0; j < 100000; ++j) {
// Include this field to increase the probability of mutation.
typename TestFixture::Message message;
message.set_optional_string(kInitialString);
message.set_optional_string("a");
mutator.Mutate(&message, 1000);

if (message.optional_string() == kIndicatorString) {
custom_mutation = true;
} else if (message.optional_string() != kInitialString) {
regular_mutation = true;
}
top_mutations.erase(message.optional_string());
nested_mutations.erase(message.mutable_sub_message()->optional_int64());
if (message.optional_string().empty()) regular_mutation = true;

if (custom_mutation && regular_mutation) break;
if (top_mutations.empty() && nested_mutations.empty() && regular_mutation)
break;
}

EXPECT_TRUE(custom_mutation);
EXPECT_TRUE(top_mutations.empty());
EXPECT_TRUE(nested_mutations.empty());
EXPECT_TRUE(regular_mutation);
}

Expand Down
2 changes: 2 additions & 0 deletions src/mutator_test_proto2.proto
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ message Msg {
Msg oneof_msg = 68;
}

optional SubMsg sub_message = 69;

required group Group = 70 {
required bool required_bool = 1;
optional bool optional_bool = 2;
Expand Down
6 changes: 6 additions & 0 deletions src/mutator_test_proto3.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ message Msg3 {
ENUM_9 = 9;
}

message SubMsg {
int64 optional_int64 = 1;
}

double optional_double = 18;
float optional_float = 19;
int32 optional_int32 = 20;
Expand Down Expand Up @@ -71,6 +75,8 @@ message Msg3 {
Msg3 oneof_msg = 68;
}

SubMsg sub_message = 69;

message EmptyMessage {}

message RegressionMessage {
Expand Down

0 comments on commit ad520b3

Please sign in to comment.