Skip to content

Commit d82b5fb

Browse files
committed
Fix message before every LLVMFuzzerTestOneInput
This is needed to make sure that corpus is still compartible. Fixed #177
1 parent 7a2ed51 commit d82b5fb

File tree

7 files changed

+90
-59
lines changed

7 files changed

+90
-59
lines changed

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,23 @@ PostProcessorRegistration can be used to avoid such issue and guide your fuzzer
9898
code. It registers callback which will be called for each message of particular type after each mutation.
9999

100100
```
101-
DEFINE_PROTO_FUZZER(const MyMessageType& input) {
102-
static PostProcessorRegistration reg = {
103-
[](MyMessageType* message, unsigned int seed) {
104-
TweakMyMessage(message, seed);
105-
}};
101+
static protobuf_mutator::libfuzzer::PostProcessorRegistration<MyMessageType> reg = {
102+
[](MyMessageType* message, unsigned int seed) {
103+
TweakMyMessage(message, seed);
104+
}};
106105
106+
DEFINE_PROTO_FUZZER(const MyMessageType& input) {
107107
// Code which needs to be fuzzed.
108108
ConsumeMyMessageType(input);
109109
}
110110
```
111111
Optional: Use seed if callback uses random numbers. It may help later with debugging.
112112

113+
Important: Callbacks should be deterministic and avoid modifying good messages.
114+
Callbacks are called for both: mutator generated and user provided inputs, like
115+
corpus or bug reproducer. So if callback performs unnecessary transformation it
116+
may corrupt the reproducer so it stops triggering the bug.
117+
113118
Note: You can add callback for any nested message and you can add multiple callbacks for
114119
the same message type.
115120
```

examples/libfuzzer/libfuzzer_bin_example.cc

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,30 @@
2121

2222
protobuf_mutator::protobuf::LogSilencer log_silincer;
2323

24-
DEFINE_BINARY_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
25-
static PostProcessorRegistration reg = {
26-
[](libfuzzer_example::Msg* message, unsigned int seed) {
27-
if (seed % 2) {
28-
message->set_optional_uint64(
29-
std::hash<std::string>{}(message->optional_string()));
30-
}
31-
32-
if (message->has_any()) {
33-
auto* any = message->mutable_any();
34-
35-
// Guide mutator to usefull 'Any' types.
36-
static const char* const expected_types[] = {
37-
"type.googleapis.com/google.protobuf.DescriptorProto",
38-
"type.googleapis.com/google.protobuf.FileDescriptorProto",
39-
};
40-
41-
if (!std::count(std::begin(expected_types), std::end(expected_types),
42-
any->type_url())) {
43-
const size_t num =
44-
(std::end(expected_types) - std::begin(expected_types));
45-
any->set_type_url(expected_types[seed % num]);
46-
}
24+
protobuf_mutator::libfuzzer::PostProcessorRegistration<libfuzzer_example::Msg>
25+
reg = {[](libfuzzer_example::Msg* message, unsigned int seed) {
26+
message->set_optional_uint64(
27+
std::hash<std::string>{}(message->optional_string()));
28+
29+
if (message->has_any()) {
30+
auto* any = message->mutable_any();
31+
32+
// Guide mutator to usefull 'Any' types.
33+
static const char* const expected_types[] = {
34+
"type.googleapis.com/google.protobuf.DescriptorProto",
35+
"type.googleapis.com/google.protobuf.FileDescriptorProto",
36+
};
37+
38+
if (!std::count(std::begin(expected_types), std::end(expected_types),
39+
any->type_url())) {
40+
const size_t num =
41+
(std::end(expected_types) - std::begin(expected_types));
42+
any->set_type_url(expected_types[seed % num]);
4743
}
48-
}};
44+
}
45+
}};
4946

47+
DEFINE_BINARY_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
5048
protobuf_mutator::protobuf::FileDescriptorProto file;
5149

5250
// Emulate a bug.

examples/libfuzzer/libfuzzer_example.cc

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,30 @@
2121

2222
protobuf_mutator::protobuf::LogSilencer log_silincer;
2323

24-
DEFINE_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
25-
static PostProcessorRegistration reg = {
26-
[](libfuzzer_example::Msg* message, unsigned int seed) {
27-
if (seed % 2) {
28-
message->set_optional_uint64(
29-
std::hash<std::string>{}(message->optional_string()));
30-
}
31-
32-
if (message->has_any()) {
33-
auto* any = message->mutable_any();
34-
35-
// Guide mutator to usefull 'Any' types.
36-
static const char* const expected_types[] = {
37-
"type.googleapis.com/google.protobuf.DescriptorProto",
38-
"type.googleapis.com/google.protobuf.FileDescriptorProto",
39-
};
40-
41-
if (!std::count(std::begin(expected_types), std::end(expected_types),
42-
any->type_url())) {
43-
const size_t num =
44-
(std::end(expected_types) - std::begin(expected_types));
45-
any->set_type_url(expected_types[seed % num]);
46-
}
24+
protobuf_mutator::libfuzzer::PostProcessorRegistration<libfuzzer_example::Msg>
25+
reg = {[](libfuzzer_example::Msg* message, unsigned int seed) {
26+
message->set_optional_uint64(
27+
std::hash<std::string>{}(message->optional_string()));
28+
29+
if (message->has_any()) {
30+
auto* any = message->mutable_any();
31+
32+
// Guide mutator to usefull 'Any' types.
33+
static const char* const expected_types[] = {
34+
"type.googleapis.com/google.protobuf.DescriptorProto",
35+
"type.googleapis.com/google.protobuf.FileDescriptorProto",
36+
};
37+
38+
if (!std::count(std::begin(expected_types), std::end(expected_types),
39+
any->type_url())) {
40+
const size_t num =
41+
(std::end(expected_types) - std::begin(expected_types));
42+
any->set_type_url(expected_types[seed % num]);
4743
}
48-
}};
44+
}
45+
}};
4946

47+
DEFINE_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
5048
protobuf_mutator::protobuf::FileDescriptorProto file;
5149

5250
// Emulate a bug.

src/libfuzzer/libfuzzer_macro.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,12 @@ size_t CustomProtoCrossOver(bool binary, const uint8_t* data1, size_t size1,
189189

190190
bool LoadProtoInput(bool binary, const uint8_t* data, size_t size,
191191
protobuf::Message* input) {
192-
return binary ? ParseBinaryMessage(data, size, input)
193-
: ParseTextMessage(data, size, input);
192+
auto result = binary ? ParseBinaryMessage(data, size, input)
193+
: ParseTextMessage(data, size, input);
194+
if (!result) return false;
195+
GetMutator()->Seed(size);
196+
GetMutator()->Fix(input);
197+
return true;
194198
}
195199

196200
void RegisterPostProcessor(

src/libfuzzer/libfuzzer_test.cc

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,26 @@
1717
#include "src/mutator_test_proto2.pb.h"
1818

1919
static bool reached = false;
20+
static bool postprocessed = false;
2021

21-
DEFINE_PROTO_FUZZER(const protobuf_mutator::Msg::EmptyMessage& message) {
22+
protobuf_mutator::libfuzzer::PostProcessorRegistration<protobuf_mutator::Msg>
23+
reg = {[](protobuf_mutator::Msg* message, unsigned int seed) {
24+
static unsigned int first_seed = seed;
25+
EXPECT_EQ(seed, first_seed);
26+
postprocessed = true;
27+
}};
28+
29+
DEFINE_TEXT_PROTO_FUZZER(const protobuf_mutator::Msg& message) {
2230
reached = true;
31+
EXPECT_TRUE(message.IsInitialized());
32+
EXPECT_TRUE(postprocessed);
2333
}
2434

25-
TEST(LibFuzzerTest, Basic) {
26-
LLVMFuzzerTestOneInput((const uint8_t*)"", 0);
27-
EXPECT_TRUE(reached);
35+
TEST(LibFuzzerTest, LLVMFuzzerTestOneInput) {
36+
for (int i = 0; i < 10; ++i) {
37+
reached = false;
38+
postprocessed = false;
39+
LLVMFuzzerTestOneInput((const uint8_t*)"", 0);
40+
EXPECT_TRUE(reached);
41+
}
2842
}

src/mutator.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,15 @@ struct CreateField : public FieldFunction<CreateField> {
625625

626626
void Mutator::Seed(uint32_t value) { random_.seed(value); }
627627

628+
void Mutator::Fix(Message* message) {
629+
UnpackedAny any;
630+
UnpackAny(*message, &any);
631+
632+
PostProcessing(keep_initialized_, post_processors_, any, &random_)
633+
.Run(message, kMaxInitializeDepth);
634+
assert(IsInitialized(*message));
635+
}
636+
628637
void Mutator::Mutate(Message* message, size_t max_size_hint) {
629638
UnpackedAny any;
630639
UnpackAny(*message, &any);

src/mutator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ class Mutator {
6060
void CrossOver(const protobuf::Message& message1, protobuf::Message* message2,
6161
size_t max_size_hint);
6262

63+
// Makes message initialized and calls post processors to make it valid.
64+
void Fix(protobuf::Message* message);
65+
6366
// Callback to postprocess mutations.
6467
// Implementation should use seed to initialize random number generators.
6568
using PostProcess =

0 commit comments

Comments
 (0)