• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/libfuzzer/libfuzzer_macro.h"
16 
17 #include <algorithm>
18 
19 #include "src/binary_format.h"
20 #include "src/libfuzzer/libfuzzer_mutator.h"
21 #include "src/text_format.h"
22 
23 namespace protobuf_mutator {
24 namespace libfuzzer {
25 
26 namespace {
27 
28 class InputReader {
29  public:
InputReader(const uint8_t * data,size_t size)30   InputReader(const uint8_t* data, size_t size) : data_(data), size_(size) {}
31   virtual ~InputReader() = default;
32 
33   virtual bool Read(protobuf::Message* message) const = 0;
34 
data() const35   const uint8_t* data() const { return data_; }
size() const36   size_t size() const { return size_; }
37 
38  private:
39   const uint8_t* data_;
40   size_t size_;
41 };
42 
43 class OutputWriter {
44  public:
OutputWriter(uint8_t * data,size_t size)45   OutputWriter(uint8_t* data, size_t size) : data_(data), size_(size) {}
46   virtual ~OutputWriter() = default;
47 
48   virtual size_t Write(const protobuf::Message& message) = 0;
49 
data() const50   uint8_t* data() const { return data_; }
size() const51   size_t size() const { return size_; }
52 
53  private:
54   uint8_t* data_;
55   size_t size_;
56 };
57 
58 class TextInputReader : public InputReader {
59  public:
60   using InputReader::InputReader;
61 
Read(protobuf::Message * message) const62   bool Read(protobuf::Message* message) const override {
63     return ParseTextMessage(data(), size(), message);
64   }
65 };
66 
67 class TextOutputWriter : public OutputWriter {
68  public:
69   using OutputWriter::OutputWriter;
70 
Write(const protobuf::Message & message)71   size_t Write(const protobuf::Message& message) override {
72     return SaveMessageAsText(message, data(), size());
73   }
74 };
75 
76 class BinaryInputReader : public InputReader {
77  public:
78   using InputReader::InputReader;
79 
Read(protobuf::Message * message) const80   bool Read(protobuf::Message* message) const override {
81     return ParseBinaryMessage(data(), size(), message);
82   }
83 };
84 
85 class BinaryOutputWriter : public OutputWriter {
86  public:
87   using OutputWriter::OutputWriter;
88 
Write(const protobuf::Message & message)89   size_t Write(const protobuf::Message& message) override {
90     return SaveMessageAsBinary(message, data(), size());
91   }
92 };
93 
GetMutator()94 Mutator* GetMutator() {
95   static Mutator mutator;
96   return &mutator;
97 }
98 
GetMaxSize(const InputReader & input,const OutputWriter & output,const protobuf::Message & message)99 size_t GetMaxSize(const InputReader& input, const OutputWriter& output,
100                   const protobuf::Message& message) {
101   size_t max_size = message.ByteSizeLong() + output.size();
102   max_size -= std::min(max_size, input.size());
103   return max_size;
104 }
105 
MutateMessage(unsigned int seed,const InputReader & input,OutputWriter * output,protobuf::Message * message)106 size_t MutateMessage(unsigned int seed, const InputReader& input,
107                      OutputWriter* output, protobuf::Message* message) {
108   GetMutator()->Seed(seed);
109   input.Read(message);
110   size_t max_size = GetMaxSize(input, *output, *message);
111   GetMutator()->Mutate(message, max_size);
112   if (size_t new_size = output->Write(*message)) {
113     assert(new_size <= output->size());
114     return new_size;
115   }
116   return 0;
117 }
118 
CrossOverMessages(unsigned int seed,const InputReader & input1,const InputReader & input2,OutputWriter * output,protobuf::Message * message1,protobuf::Message * message2)119 size_t CrossOverMessages(unsigned int seed, const InputReader& input1,
120                          const InputReader& input2, OutputWriter* output,
121                          protobuf::Message* message1,
122                          protobuf::Message* message2) {
123   GetMutator()->Seed(seed);
124   input1.Read(message1);
125   input2.Read(message2);
126   size_t max_size = GetMaxSize(input1, *output, *message1);
127   GetMutator()->CrossOver(*message2, message1, max_size);
128   if (size_t new_size = output->Write(*message1)) {
129     assert(new_size <= output->size());
130     return new_size;
131   }
132   return 0;
133 }
134 
MutateTextMessage(uint8_t * data,size_t size,size_t max_size,unsigned int seed,protobuf::Message * message)135 size_t MutateTextMessage(uint8_t* data, size_t size, size_t max_size,
136                          unsigned int seed, protobuf::Message* message) {
137   TextInputReader input(data, size);
138   TextOutputWriter output(data, max_size);
139   return MutateMessage(seed, input, &output, message);
140 }
141 
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)142 size_t CrossOverTextMessages(const uint8_t* data1, size_t size1,
143                              const uint8_t* data2, size_t size2, uint8_t* out,
144                              size_t max_out_size, unsigned int seed,
145                              protobuf::Message* message1,
146                              protobuf::Message* message2) {
147   TextInputReader input1(data1, size1);
148   TextInputReader input2(data2, size2);
149   TextOutputWriter output(out, max_out_size);
150   return CrossOverMessages(seed, input1, input2, &output, message1, message2);
151 }
152 
MutateBinaryMessage(uint8_t * data,size_t size,size_t max_size,unsigned int seed,protobuf::Message * message)153 size_t MutateBinaryMessage(uint8_t* data, size_t size, size_t max_size,
154                            unsigned int seed, protobuf::Message* message) {
155   BinaryInputReader input(data, size);
156   BinaryOutputWriter output(data, max_size);
157   return MutateMessage(seed, input, &output, message);
158 }
159 
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)160 size_t CrossOverBinaryMessages(const uint8_t* data1, size_t size1,
161                                const uint8_t* data2, size_t size2, uint8_t* out,
162                                size_t max_out_size, unsigned int seed,
163                                protobuf::Message* message1,
164                                protobuf::Message* message2) {
165   BinaryInputReader input1(data1, size1);
166   BinaryInputReader input2(data2, size2);
167   BinaryOutputWriter output(out, max_out_size);
168   return CrossOverMessages(seed, input1, input2, &output, message1, message2);
169 }
170 
171 }  // namespace
172 
CustomProtoMutator(bool binary,uint8_t * data,size_t size,size_t max_size,unsigned int seed,protobuf::Message * input)173 size_t CustomProtoMutator(bool binary, uint8_t* data, size_t size,
174                           size_t max_size, unsigned int seed,
175                           protobuf::Message* input) {
176   auto mutate = binary ? &MutateBinaryMessage : &MutateTextMessage;
177   return mutate(data, size, max_size, seed, input);
178 }
179 
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)180 size_t CustomProtoCrossOver(bool binary, const uint8_t* data1, size_t size1,
181                             const uint8_t* data2, size_t size2, uint8_t* out,
182                             size_t max_out_size, unsigned int seed,
183                             protobuf::Message* input1,
184                             protobuf::Message* input2) {
185   auto cross = binary ? &CrossOverBinaryMessages : &CrossOverTextMessages;
186   return cross(data1, size1, data2, size2, out, max_out_size, seed, input1,
187                input2);
188 }
189 
LoadProtoInput(bool binary,const uint8_t * data,size_t size,protobuf::Message * input)190 bool LoadProtoInput(bool binary, const uint8_t* data, size_t size,
191                     protobuf::Message* input) {
192   return binary ? ParseBinaryMessage(data, size, input)
193                 : ParseTextMessage(data, size, input);
194 }
195 
RegisterPostProcessor(const protobuf::Descriptor * desc,std::function<void (protobuf::Message * message,unsigned int seed)> callback)196 void RegisterPostProcessor(
197     const protobuf::Descriptor* desc,
198     std::function<void(protobuf::Message* message, unsigned int seed)>
199         callback) {
200   GetMutator()->RegisterPostProcessor(desc, callback);
201 }
202 
203 }  // namespace libfuzzer
204 }  // namespace protobuf_mutator
205