• 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 #include <memory>
19 #include <vector>
20 
21 #include "src/binary_format.h"
22 #include "src/libfuzzer/libfuzzer_mutator.h"
23 #include "src/text_format.h"
24 
25 namespace protobuf_mutator {
26 namespace libfuzzer {
27 
28 namespace {
29 
30 class InputReader {
31  public:
InputReader(const uint8_t * data,size_t size)32   InputReader(const uint8_t* data, size_t size) : data_(data), size_(size) {}
33   virtual ~InputReader() = default;
34 
35   virtual bool Read(protobuf::Message* message) const = 0;
36 
data() const37   const uint8_t* data() const { return data_; }
size() const38   size_t size() const { return size_; }
39 
40  private:
41   const uint8_t* data_;
42   size_t size_;
43 };
44 
45 class OutputWriter {
46  public:
OutputWriter(uint8_t * data,size_t size)47   OutputWriter(uint8_t* data, size_t size) : data_(data), size_(size) {}
48   virtual ~OutputWriter() = default;
49 
50   virtual size_t Write(const protobuf::Message& message) = 0;
51 
data() const52   uint8_t* data() const { return data_; }
size() const53   size_t size() const { return size_; }
54 
55  private:
56   uint8_t* data_;
57   size_t size_;
58 };
59 
60 class TextInputReader : public InputReader {
61  public:
62   using InputReader::InputReader;
63 
Read(protobuf::Message * message) const64   bool Read(protobuf::Message* message) const override {
65     return ParseTextMessage(data(), size(), message);
66   }
67 };
68 
69 class TextOutputWriter : public OutputWriter {
70  public:
71   using OutputWriter::OutputWriter;
72 
Write(const protobuf::Message & message)73   size_t Write(const protobuf::Message& message) override {
74     return SaveMessageAsText(message, data(), size());
75   }
76 };
77 
78 class BinaryInputReader : public InputReader {
79  public:
80   using InputReader::InputReader;
81 
Read(protobuf::Message * message) const82   bool Read(protobuf::Message* message) const override {
83     return ParseBinaryMessage(data(), size(), message);
84   }
85 };
86 
87 class BinaryOutputWriter : public OutputWriter {
88  public:
89   using OutputWriter::OutputWriter;
90 
Write(const protobuf::Message & message)91   size_t Write(const protobuf::Message& message) override {
92     return SaveMessageAsBinary(message, data(), size());
93   }
94 };
95 
96 class LastMutationCache {
97  public:
Store(const uint8_t * data,size_t size,protobuf::Message * message)98   void Store(const uint8_t* data, size_t size, protobuf::Message* message) {
99     if (!message_) message_.reset(message->New());
100     message->GetReflection()->Swap(message, message_.get());
101     data_.assign(data, data + size);
102   }
103 
LoadIfSame(const uint8_t * data,size_t size,protobuf::Message * message)104   bool LoadIfSame(const uint8_t* data, size_t size,
105                   protobuf::Message* message) {
106     if (!message_ || size != data_.size() ||
107         !std::equal(data_.begin(), data_.end(), data))
108       return false;
109 
110     message->GetReflection()->Swap(message, message_.get());
111     message_.reset();
112     return true;
113   }
114 
115  private:
116   std::vector<uint8_t> data_;
117   std::unique_ptr<protobuf::Message> message_;
118 };
119 
GetCache()120 LastMutationCache* GetCache() {
121   static LastMutationCache cache;
122   return &cache;
123 }
124 
GetMutator()125 Mutator* GetMutator() {
126   static Mutator mutator;
127   return &mutator;
128 }
129 
GetMaxSize(const InputReader & input,const OutputWriter & output,const protobuf::Message & message)130 size_t GetMaxSize(const InputReader& input, const OutputWriter& output,
131                   const protobuf::Message& message) {
132   size_t max_size = message.ByteSizeLong() + output.size();
133   max_size -= std::min(max_size, input.size());
134   return max_size;
135 }
136 
MutateMessage(unsigned int seed,const InputReader & input,OutputWriter * output,protobuf::Message * message)137 size_t MutateMessage(unsigned int seed, const InputReader& input,
138                      OutputWriter* output, protobuf::Message* message) {
139   GetMutator()->Seed(seed);
140   input.Read(message);
141   size_t max_size = GetMaxSize(input, *output, *message);
142   GetMutator()->Mutate(message, max_size);
143   if (size_t new_size = output->Write(*message)) {
144     assert(new_size <= output->size());
145     GetCache()->Store(output->data(), new_size, message);
146     return new_size;
147   }
148   return 0;
149 }
150 
CrossOverMessages(unsigned int seed,const InputReader & input1,const InputReader & input2,OutputWriter * output,protobuf::Message * message1,protobuf::Message * message2)151 size_t CrossOverMessages(unsigned int seed, const InputReader& input1,
152                          const InputReader& input2, OutputWriter* output,
153                          protobuf::Message* message1,
154                          protobuf::Message* message2) {
155   GetMutator()->Seed(seed);
156   input1.Read(message1);
157   input2.Read(message2);
158   size_t max_size = GetMaxSize(input1, *output, *message1);
159   GetMutator()->CrossOver(*message2, message1, max_size);
160   if (size_t new_size = output->Write(*message1)) {
161     assert(new_size <= output->size());
162     GetCache()->Store(output->data(), new_size, message1);
163     return new_size;
164   }
165   return 0;
166 }
167 
MutateTextMessage(uint8_t * data,size_t size,size_t max_size,unsigned int seed,protobuf::Message * message)168 size_t MutateTextMessage(uint8_t* data, size_t size, size_t max_size,
169                          unsigned int seed, protobuf::Message* message) {
170   TextInputReader input(data, size);
171   TextOutputWriter output(data, max_size);
172   return MutateMessage(seed, input, &output, message);
173 }
174 
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)175 size_t CrossOverTextMessages(const uint8_t* data1, size_t size1,
176                              const uint8_t* data2, size_t size2, uint8_t* out,
177                              size_t max_out_size, unsigned int seed,
178                              protobuf::Message* message1,
179                              protobuf::Message* message2) {
180   TextInputReader input1(data1, size1);
181   TextInputReader input2(data2, size2);
182   TextOutputWriter output(out, max_out_size);
183   return CrossOverMessages(seed, input1, input2, &output, message1, message2);
184 }
185 
MutateBinaryMessage(uint8_t * data,size_t size,size_t max_size,unsigned int seed,protobuf::Message * message)186 size_t MutateBinaryMessage(uint8_t* data, size_t size, size_t max_size,
187                            unsigned int seed, protobuf::Message* message) {
188   BinaryInputReader input(data, size);
189   BinaryOutputWriter output(data, max_size);
190   return MutateMessage(seed, input, &output, message);
191 }
192 
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)193 size_t CrossOverBinaryMessages(const uint8_t* data1, size_t size1,
194                                const uint8_t* data2, size_t size2, uint8_t* out,
195                                size_t max_out_size, unsigned int seed,
196                                protobuf::Message* message1,
197                                protobuf::Message* message2) {
198   BinaryInputReader input1(data1, size1);
199   BinaryInputReader input2(data2, size2);
200   BinaryOutputWriter output(out, max_out_size);
201   return CrossOverMessages(seed, input1, input2, &output, message1, message2);
202 }
203 
204 }  // namespace
205 
CustomProtoMutator(bool binary,uint8_t * data,size_t size,size_t max_size,unsigned int seed,protobuf::Message * input)206 size_t CustomProtoMutator(bool binary, uint8_t* data, size_t size,
207                           size_t max_size, unsigned int seed,
208                           protobuf::Message* input) {
209   auto mutate = binary ? &MutateBinaryMessage : &MutateTextMessage;
210   return mutate(data, size, max_size, seed, input);
211 }
212 
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)213 size_t CustomProtoCrossOver(bool binary, const uint8_t* data1, size_t size1,
214                             const uint8_t* data2, size_t size2, uint8_t* out,
215                             size_t max_out_size, unsigned int seed,
216                             protobuf::Message* input1,
217                             protobuf::Message* input2) {
218   auto cross = binary ? &CrossOverBinaryMessages : &CrossOverTextMessages;
219   return cross(data1, size1, data2, size2, out, max_out_size, seed, input1,
220                input2);
221 }
222 
LoadProtoInput(bool binary,const uint8_t * data,size_t size,protobuf::Message * input)223 bool LoadProtoInput(bool binary, const uint8_t* data, size_t size,
224                     protobuf::Message* input) {
225   if (GetCache()->LoadIfSame(data, size, input)) return true;
226   auto result = binary ? ParseBinaryMessage(data, size, input)
227                        : ParseTextMessage(data, size, input);
228   if (!result) return false;
229   GetMutator()->Seed(size);
230   GetMutator()->Fix(input);
231   return true;
232 }
233 
RegisterPostProcessor(const protobuf::Descriptor * desc,std::function<void (protobuf::Message * message,unsigned int seed)> callback)234 void RegisterPostProcessor(
235     const protobuf::Descriptor* desc,
236     std::function<void(protobuf::Message* message, unsigned int seed)>
237         callback) {
238   GetMutator()->RegisterPostProcessor(desc, callback);
239 }
240 
241 }  // namespace libfuzzer
242 }  // namespace protobuf_mutator
243