| // 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 "src/libfuzzer/libfuzzer_macro.h" |
| |
| #include "src/binary_format.h" |
| #include "src/libfuzzer/libfuzzer_mutator.h" |
| #include "src/text_format.h" |
| |
| namespace protobuf_mutator { |
| namespace libfuzzer { |
| |
| namespace { |
| |
| class InputReader { |
| public: |
| InputReader(const uint8_t* data, size_t size) : data_(data), size_(size) {} |
| virtual ~InputReader() = default; |
| |
| virtual bool Read(protobuf::Message* message) const = 0; |
| |
| const uint8_t* data() const { return data_; } |
| size_t size() const { return size_; } |
| |
| private: |
| const uint8_t* data_; |
| size_t size_; |
| }; |
| |
| class OutputWriter { |
| public: |
| OutputWriter(uint8_t* data, size_t size) : data_(data), size_(size) {} |
| virtual ~OutputWriter() = default; |
| |
| virtual size_t Write(const protobuf::Message& message) = 0; |
| |
| uint8_t* data() const { return data_; } |
| size_t size() const { return size_; } |
| |
| private: |
| uint8_t* data_; |
| size_t size_; |
| }; |
| |
| class TextInputReader : public InputReader { |
| public: |
| using InputReader::InputReader; |
| |
| bool Read(protobuf::Message* message) const override { |
| return ParseTextMessage(data(), size(), message); |
| } |
| }; |
| |
| class TextOutputWriter : public OutputWriter { |
| public: |
| using OutputWriter::OutputWriter; |
| |
| size_t Write(const protobuf::Message& message) override { |
| return SaveMessageAsText(message, data(), size()); |
| } |
| }; |
| |
| 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, protobuf::Message* message) { |
| RandomEngine random(seed); |
| Mutator mutator(&random); |
| input.Read(message); |
| mutator.Mutate(message, output->size() > input.size() |
| ? (output->size() - input.size()) |
| : 0); |
| if (size_t new_size = output->Write(*message)) { |
| assert(new_size <= output->size()); |
| return new_size; |
| } |
| return 0; |
| } |
| |
| size_t CrossOverMessages(unsigned int seed, const InputReader& input1, |
| const InputReader& input2, OutputWriter* output, |
| protobuf::Message* message1, |
| protobuf::Message* message2) { |
| RandomEngine random(seed); |
| Mutator mutator(&random); |
| input1.Read(message1); |
| input2.Read(message2); |
| mutator.CrossOver(*message2, message1); |
| if (size_t new_size = output->Write(*message1)) { |
| assert(new_size <= output->size()); |
| return new_size; |
| } |
| return 0; |
| } |
| |
| 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 MutateMessage(seed, input, &output, message); |
| } |
| |
| size_t CrossOverTextMessages(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) { |
| TextInputReader input1(data1, size1); |
| TextInputReader input2(data2, size2); |
| TextOutputWriter output(out, max_out_size); |
| 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 |
| |
| size_t CustomProtoMutator(bool binary, uint8_t* data, size_t size, |
| size_t max_size, unsigned int seed, |
| protobuf::Message* input) { |
| auto mutate = binary ? &MutateBinaryMessage : &MutateTextMessage; |
| return mutate(data, size, max_size, seed, input); |
| } |
| |
| size_t CustomProtoCrossOver(bool binary, 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* input1, |
| protobuf::Message* input2) { |
| auto cross = binary ? &CrossOverBinaryMessages : &CrossOverTextMessages; |
| return cross(data1, size1, data2, size2, out, max_out_size, seed, input1, |
| input2); |
| } |
| |
| bool LoadProtoInput(bool binary, const uint8_t* data, size_t size, |
| protobuf::Message* input) { |
| return binary ? ParseBinaryMessage(data, size, input) |
| : ParseTextMessage(data, size, input); |
| } |
| |
| } // namespace libfuzzer |
| } // namespace protobuf_mutator |