Support binary serialization of protos.
diff --git a/examples/libfuzzer/CMakeLists.txt b/examples/libfuzzer/CMakeLists.txt
index 173363c..f6f2dbf 100644
--- a/examples/libfuzzer/CMakeLists.txt
+++ b/examples/libfuzzer/CMakeLists.txt
@@ -12,26 +12,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-protobuf_generate_cpp(LIB_FUZZER_EXAMPLE_PROTO_SRCS
- LIB_FUZZER_EXAMPLE_PROTO_HDRS
- libfuzzer_example.proto)
-add_executable(libfuzzer_example
- libfuzzer_example.cc
- ${LIB_FUZZER_EXAMPLE_PROTO_SRCS})
-target_link_libraries(libfuzzer_example
- protobuf-mutator
- ${LIB_PROTO_MUTATOR_FUZZER_LIBRARIES})
-set_property(TARGET libfuzzer_example
- PROPERTY COMPILE_FLAGS ${FUZZING_FLAGS})
-set_property(TARGET libfuzzer_example
- PROPERTY LINK_FLAGS ${FUZZING_FLAGS})
-
add_executable(libfuzzer_example_test
libfuzzer_example_test.cc)
-add_dependencies(libfuzzer_example_test libfuzzer_example)
target_link_libraries(libfuzzer_example_test
${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT})
add_test(test.libfuzzer_example_test libfuzzer_example_test --gtest_color=yes AUTO)
-
add_dependencies(check libfuzzer_example_test)
+
+protobuf_generate_cpp(LIB_FUZZER_EXAMPLE_PROTO_SRCS
+ LIB_FUZZER_EXAMPLE_PROTO_HDRS
+ libfuzzer_example.proto)
+
+add_library(fuzzer-example-proto
+ ${LIB_FUZZER_EXAMPLE_PROTO_SRCS})
+set_property(TARGET fuzzer-example-proto
+ PROPERTY COMPILE_FLAGS ${NO_FUZZING_FLAGS})
+
+foreach(fuzzer libfuzzer_example libfuzzer_bin_example)
+ add_executable(${fuzzer} ${fuzzer}.cc)
+ target_link_libraries(${fuzzer}
+ fuzzer-example-proto
+ protobuf-mutator
+ ${LIB_PROTO_MUTATOR_FUZZER_LIBRARIES})
+ set_property(TARGET ${fuzzer}
+ PROPERTY COMPILE_FLAGS ${FUZZING_FLAGS})
+ set_property(TARGET ${fuzzer}
+ PROPERTY LINK_FLAGS ${FUZZING_FLAGS})
+ add_dependencies(libfuzzer_example_test ${fuzzer})
+endforeach(fuzzer)
diff --git a/examples/libfuzzer/libfuzzer_bin_example.cc b/examples/libfuzzer/libfuzzer_bin_example.cc
new file mode 100644
index 0000000..87dd7fd
--- /dev/null
+++ b/examples/libfuzzer/libfuzzer_bin_example.cc
@@ -0,0 +1,47 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstddef>
+#include <cstdint>
+
+#include "examples/libfuzzer/libfuzzer_example.pb.h"
+#include "src/libfuzzer_protobuf_mutator.h"
+
+using libfuzzer_example::Msg;
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
+ size_t max_size, unsigned int seed) {
+ return protobuf_mutator::MutateBinaryMessage<Msg>(data, size, max_size, seed);
+}
+
+extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t* data1, size_t size1,
+ const uint8_t* data2, size_t size2,
+ uint8_t* out, size_t max_out_size,
+ unsigned int seed) {
+ return protobuf_mutator::CrossOverBinaryMessages<Msg>(
+ data1, size1, data2, size2, out, max_out_size, seed);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ libfuzzer_example::Msg message;
+ protobuf_mutator::ParseBinaryMessage(data, size, &message);
+
+ // Emulate a bug.
+ if (message.optional_bool() &&
+ std::hash<std::string>()(message.optional_string()) % 1000 == 9) {
+ abort();
+ }
+
+ return 0;
+}
diff --git a/examples/libfuzzer/libfuzzer_example_test.cc b/examples/libfuzzer/libfuzzer_example_test.cc
index 8401a9e..a142553 100644
--- a/examples/libfuzzer/libfuzzer_example_test.cc
+++ b/examples/libfuzzer/libfuzzer_example_test.cc
@@ -32,3 +32,17 @@
// Cleanup.
EXPECT_EQ(0, std::system((std::string("rm -rf ") + dir).c_str()));
}
+
+TEST(LibFuzzerExampleTest, CrashBinary) {
+ char dir_template[] = "/tmp/libfuzzer_example_test_XXXXXX";
+ auto dir = mkdtemp(dir_template);
+ ASSERT_TRUE(dir);
+
+ std::string cmd = "./libfuzzer_bin_example -max_len=150 -artifact_prefix=" +
+ std::string(dir) + "/ " + dir + "/";
+ int retvalue = std::system(cmd.c_str());
+ EXPECT_EQ(kDefaultLibFuzzerError, WSTOPSIG(retvalue));
+
+ // Cleanup.
+ EXPECT_EQ(0, std::system((std::string("rm -rf ") + dir).c_str()));
+}
diff --git a/port/protobuf.h b/port/protobuf.h
index a0311df..6c5bc78 100644
--- a/port/protobuf.h
+++ b/port/protobuf.h
@@ -32,6 +32,13 @@
if (!protobuf::TextFormat::PrintToString(message, &tmp)) return {};
return tmp;
}
+
+inline std::string MessageToBinaryString(const protobuf::Message& message) {
+ std::string tmp;
+ if (!message.SerializeToString(&tmp)) return {};
+ return tmp;
+}
+
} // namespace protobuf_mutator
#endif // PORT_PROTOBUF_H_
diff --git a/src/libfuzzer_protobuf_mutator.cc b/src/libfuzzer_protobuf_mutator.cc
index ffe4e41..8445866 100644
--- a/src/libfuzzer_protobuf_mutator.cc
+++ b/src/libfuzzer_protobuf_mutator.cc
@@ -88,8 +88,26 @@
}
};
-size_t MutateTextMessage(unsigned int seed, const InputReader& input,
- OutputWriter* output, Message* message) {
+class BinaryInputReader : public InputReader {
+ public:
+ using InputReader::InputReader;
+
+ bool Read(protobuf::Message* message) const override {
+ return ParseBinaryMessage(data(), size(), message);
+ }
+};
+
+class BinaryOutputWriter : public OutputWriter {
+ public:
+ using OutputWriter::OutputWriter;
+
+ size_t Write(const protobuf::Message& message) override {
+ return SaveMessageAsBinary(message, data(), size());
+ }
+};
+
+size_t MutateMessage(unsigned int seed, const InputReader& input,
+ OutputWriter* output, Message* message) {
protobuf_mutator::LibFuzzerProtobufMutator mutator(seed);
for (int i = 0; i < 100; ++i) {
input.Read(message);
@@ -104,10 +122,10 @@
return 0;
}
-size_t CrossOverTextMessages(unsigned int seed, const InputReader& input1,
- const InputReader& input2, OutputWriter* output,
- protobuf::Message* message1,
- protobuf::Message* message2) {
+size_t CrossOverMessages(unsigned int seed, const InputReader& input1,
+ const InputReader& input2, OutputWriter* output,
+ protobuf::Message* message1,
+ protobuf::Message* message2) {
protobuf_mutator::LibFuzzerProtobufMutator mutator(seed);
input2.Read(message2);
for (int i = 0; i < 100; ++i) {
@@ -184,13 +202,36 @@
return MessageToTextString(message);
}
+bool ParseBinaryMessage(const uint8_t* data, size_t size, Message* output) {
+ return ParseBinaryMessage({data, data + size}, output);
+}
+
+bool ParseBinaryMessage(const std::string& data, protobuf::Message* output) {
+ output->Clear();
+ return output->ParsePartialFromString(data);
+}
+
+size_t SaveMessageAsBinary(const Message& message, uint8_t* data,
+ size_t max_size) {
+ std::string result = SaveMessageAsBinary(message);
+ if (result.size() <= max_size) {
+ memcpy(data, result.data(), result.size());
+ return result.size();
+ }
+ return 0;
+}
+
+std::string SaveMessageAsBinary(const protobuf::Message& message) {
+ return MessageToBinaryString(message);
+}
+
namespace internal {
size_t MutateTextMessage(uint8_t* data, size_t size, size_t max_size,
unsigned int seed, protobuf::Message* message) {
TextInputReader input(data, size);
TextOutputWriter output(data, max_size);
- return MutateTextMessage(seed, input, &output, message);
+ return MutateMessage(seed, input, &output, message);
}
size_t CrossOverTextMessages(const uint8_t* data1, size_t size1,
@@ -201,8 +242,25 @@
TextInputReader input1(data1, size1);
TextInputReader input2(data2, size2);
TextOutputWriter output(out, max_out_size);
- return CrossOverTextMessages(seed, input1, input2, &output, message1,
- message2);
+ return CrossOverMessages(seed, input1, input2, &output, message1, message2);
+}
+
+size_t MutateBinaryMessage(uint8_t* data, size_t size, size_t max_size,
+ unsigned int seed, protobuf::Message* message) {
+ BinaryInputReader input(data, size);
+ BinaryOutputWriter output(data, max_size);
+ return MutateMessage(seed, input, &output, message);
+}
+
+size_t CrossOverBinaryMessages(const uint8_t* data1, size_t size1,
+ const uint8_t* data2, size_t size2, uint8_t* out,
+ size_t max_out_size, unsigned int seed,
+ protobuf::Message* message1,
+ protobuf::Message* message2) {
+ BinaryInputReader input1(data1, size1);
+ BinaryInputReader input2(data2, size2);
+ BinaryOutputWriter output(out, max_out_size);
+ return CrossOverMessages(seed, input1, input2, &output, message1, message2);
}
} // namespace internal
diff --git a/src/libfuzzer_protobuf_mutator.h b/src/libfuzzer_protobuf_mutator.h
index 59aea68..9d1cd38 100644
--- a/src/libfuzzer_protobuf_mutator.h
+++ b/src/libfuzzer_protobuf_mutator.h
@@ -50,6 +50,14 @@
size_t max_size);
std::string SaveMessageAsText(const protobuf::Message& message);
+// Same as above but for binary serialization.
+bool ParseBinaryMessage(const uint8_t* data, size_t size,
+ protobuf::Message* output);
+bool ParseBinaryMessage(const std::string& data, protobuf::Message* output);
+size_t SaveMessageAsBinary(const protobuf::Message& message, uint8_t* data,
+ size_t max_size);
+std::string SaveMessageAsBinary(const protobuf::Message& message);
+
namespace internal {
size_t MutateTextMessage(uint8_t* data, size_t size, size_t max_size,
unsigned int seed, protobuf::Message* message);
@@ -58,6 +66,13 @@
size_t max_out_size, unsigned int seed,
protobuf::Message* message1,
protobuf::Message* message2);
+size_t MutateBinaryMessage(uint8_t* data, size_t size, size_t max_size,
+ unsigned int seed, protobuf::Message* message);
+size_t CrossOverBinaryMessages(const uint8_t* data1, size_t size1,
+ const uint8_t* data2, size_t size2, uint8_t* out,
+ size_t max_out_size, unsigned int seed,
+ protobuf::Message* message1,
+ protobuf::Message* message2);
} // namespace internal
// Mutates proto serialized as text.
@@ -80,6 +95,26 @@
&message2);
}
+// Mutates proto serialized as binary.
+template <class MessageType>
+size_t MutateBinaryMessage(uint8_t* data, size_t size, size_t max_size,
+ unsigned int seed) {
+ MessageType message;
+ return internal::MutateBinaryMessage(data, size, max_size, seed, &message);
+}
+
+// Crossover two protos serialized as binary.
+template <class MessageType>
+size_t CrossOverBinaryMessages(const uint8_t* data1, size_t size1,
+ const uint8_t* data2, size_t size2, uint8_t* out,
+ size_t max_out_size, unsigned int seed) {
+ MessageType message1;
+ MessageType message2;
+ return internal::CrossOverBinaryMessages(data1, size1, data2, size2, out,
+ max_out_size, seed, &message1,
+ &message2);
+}
+
} // namespace protobuf_mutator
#endif // SRC_LIBFUZZER_PROTOBUF_MUTATOR_H_