xref: /aosp_15_r20/external/pigweed/pw_protobuf/encoder_fuzzer.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2019 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://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, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include <cstddef>
16 #include <cstdint>
17 #include <cstring>
18 #include <vector>
19 
20 #include "fuzz.h"
21 #include "pw_fuzzer/asan_interface.h"
22 #include "pw_fuzzer/fuzzed_data_provider.h"
23 #include "pw_protobuf/encoder.h"
24 #include "pw_span/span.h"
25 
26 namespace pw::protobuf::fuzz {
27 namespace {
28 
29 // TODO: b/235289495 - Move this to pw_fuzzer/fuzzed_data_provider.h
30 
31 // Uses the given |provider| to pick and return a number between 0 and the
32 // maximum numbers of T that can be generated from the remaining input data.
33 template <typename T>
ConsumeSize(FuzzedDataProvider & provider)34 size_t ConsumeSize(FuzzedDataProvider& provider) {
35   size_t max = provider.remaining_bytes() / sizeof(T);
36   return provider.ConsumeIntegralInRange<size_t>(0, max);
37 }
38 
39 // Uses the given |provider| to generate several instances of T, store them in
40 // |data|, and then return a span to them. It is the caller's responsbility
41 // to ensure |data| remains in scope as long as the returned span.
42 template <typename T>
ConsumeSpan(FuzzedDataProvider & provider,std::vector<T> * data)43 span<const T> ConsumeSpan(FuzzedDataProvider& provider, std::vector<T>* data) {
44   size_t num = ConsumeSize<T>(provider);
45   size_t off = data->size();
46   if (num == 0) {
47     return span<const T>();
48   }
49 
50   data->reserve(off + num);
51   for (size_t i = 0; i < num; ++i) {
52     if constexpr (std::is_floating_point<T>::value) {
53       data->push_back(provider.ConsumeFloatingPoint<T>());
54     } else {
55       data->push_back(provider.ConsumeIntegral<T>());
56     }
57   }
58   return span(&((*data)[off]), num);
59 }
60 
61 // Uses the given |provider| to generate a string, store it in |data|, and
62 // return a C-style representation. It is the caller's responsbility to
63 // ensure |data| remains in scope as long as the returned char*.
ConsumeString(FuzzedDataProvider & provider,std::vector<std::string> * data)64 const char* ConsumeString(FuzzedDataProvider& provider,
65                           std::vector<std::string>* data) {
66   size_t off = data->size();
67   // OSS-Fuzz's clang doesn't have the zero-parameter version of
68   // ConsumeRandomLengthString yet.
69   size_t max_length = std::numeric_limits<size_t>::max();
70   data->push_back(provider.ConsumeRandomLengthString(max_length));
71   return (*data)[off].c_str();
72 }
73 
74 // Uses the given |provider| to generate non-arithmetic bytes, store them in
75 // |data|, and return a span to them. It is the caller's responsbility to
76 // ensure |data| remains in scope as long as the returned span.
ConsumeBytes(FuzzedDataProvider & provider,std::vector<std::byte> * data)77 span<const std::byte> ConsumeBytes(FuzzedDataProvider& provider,
78                                    std::vector<std::byte>* data) {
79   size_t num = ConsumeSize<std::byte>(provider);
80   auto added = provider.ConsumeBytes<std::byte>(num);
81   size_t off = data->size();
82   num = added.size();
83   data->insert(data->end(), added.begin(), added.end());
84   // It's possible nothing was added, and the vector was empty to begin with.
85   if (data->empty()) {
86     return span<const std::byte>();
87   }
88   return span(&((*data)[off]), num);
89 }
90 
RecursiveFuzzedEncode(FuzzedDataProvider & provider,StreamEncoder & encoder,uint32_t depth=0)91 void RecursiveFuzzedEncode(FuzzedDataProvider& provider,
92                            StreamEncoder& encoder,
93                            uint32_t depth = 0) {
94   constexpr size_t kMaxDepth = 256;
95   if (depth > kMaxDepth) {
96     return;
97   }
98 
99   // Storage for generated spans
100   std::vector<uint32_t> u32s;
101   std::vector<uint64_t> u64s;
102   std::vector<int32_t> s32s;
103   std::vector<int64_t> s64s;
104   std::vector<float> floats;
105   std::vector<double> doubles;
106   std::vector<std::string> strings;
107   std::vector<std::byte> bytes;
108 
109   // Consume the fuzzing input, using it to generate a sequence of fields to
110   // encode. Both the uint32_t field IDs and the fields values are generated.
111   // Don't try to detect errors, ensures pushes and pops are balanced, or
112   // otherwise hold the interface correctly. Instead, fuzz the widest possbile
113   // set of inputs to the encoder to ensure it doesn't misbehave.
114   while (provider.remaining_bytes() != 0) {
115     switch (provider.ConsumeEnum<FieldType>()) {
116       case kUint32:
117         encoder
118             .WriteUint32(provider.ConsumeIntegral<uint32_t>(),
119                          provider.ConsumeIntegral<uint32_t>())
120             .IgnoreError();
121         break;
122       case kPackedUint32:
123         encoder
124             .WritePackedUint32(provider.ConsumeIntegral<uint32_t>(),
125                                ConsumeSpan<uint32_t>(provider, &u32s))
126             .IgnoreError();
127         break;
128       case kUint64:
129         encoder
130             .WriteUint64(provider.ConsumeIntegral<uint32_t>(),
131                          provider.ConsumeIntegral<uint64_t>())
132             .IgnoreError();
133         break;
134       case kPackedUint64:
135         encoder
136             .WritePackedUint64(provider.ConsumeIntegral<uint32_t>(),
137                                ConsumeSpan<uint64_t>(provider, &u64s))
138             .IgnoreError();
139         break;
140       case kInt32:
141         encoder
142             .WriteInt32(provider.ConsumeIntegral<uint32_t>(),
143                         provider.ConsumeIntegral<int32_t>())
144             .IgnoreError();
145         break;
146       case kPackedInt32:
147         encoder
148             .WritePackedInt32(provider.ConsumeIntegral<uint32_t>(),
149                               ConsumeSpan<int32_t>(provider, &s32s))
150             .IgnoreError();
151         break;
152       case kInt64:
153         encoder
154             .WriteInt64(provider.ConsumeIntegral<uint32_t>(),
155                         provider.ConsumeIntegral<int64_t>())
156             .IgnoreError();
157         break;
158       case kPackedInt64:
159         encoder
160             .WritePackedInt64(provider.ConsumeIntegral<uint32_t>(),
161                               ConsumeSpan<int64_t>(provider, &s64s))
162             .IgnoreError();
163         break;
164       case kSint32:
165         encoder
166             .WriteSint32(provider.ConsumeIntegral<uint32_t>(),
167                          provider.ConsumeIntegral<int32_t>())
168             .IgnoreError();
169         break;
170       case kPackedSint32:
171         encoder
172             .WritePackedSint32(provider.ConsumeIntegral<uint32_t>(),
173                                ConsumeSpan<int32_t>(provider, &s32s))
174             .IgnoreError();
175         break;
176       case kSint64:
177         encoder
178             .WriteSint64(provider.ConsumeIntegral<uint32_t>(),
179                          provider.ConsumeIntegral<int64_t>())
180             .IgnoreError();
181         break;
182       case kPackedSint64:
183         encoder
184             .WritePackedSint64(provider.ConsumeIntegral<uint32_t>(),
185                                ConsumeSpan<int64_t>(provider, &s64s))
186             .IgnoreError();
187         break;
188       case kBool:
189         encoder
190             .WriteBool(provider.ConsumeIntegral<uint32_t>(),
191                        provider.ConsumeBool())
192             .IgnoreError();
193         break;
194       case kFixed32:
195         encoder
196             .WriteFixed32(provider.ConsumeIntegral<uint32_t>(),
197                           provider.ConsumeIntegral<uint32_t>())
198             .IgnoreError();
199         break;
200       case kPackedFixed32:
201         encoder
202             .WritePackedFixed32(provider.ConsumeIntegral<uint32_t>(),
203                                 ConsumeSpan<uint32_t>(provider, &u32s))
204             .IgnoreError();
205         break;
206       case kFixed64:
207         encoder
208             .WriteFixed64(provider.ConsumeIntegral<uint32_t>(),
209                           provider.ConsumeIntegral<uint64_t>())
210             .IgnoreError();
211         break;
212       case kPackedFixed64:
213         encoder
214             .WritePackedFixed64(provider.ConsumeIntegral<uint32_t>(),
215                                 ConsumeSpan<uint64_t>(provider, &u64s))
216             .IgnoreError();
217         break;
218       case kSfixed32:
219         encoder
220             .WriteSfixed32(provider.ConsumeIntegral<uint32_t>(),
221                            provider.ConsumeIntegral<int32_t>())
222             .IgnoreError();
223         break;
224       case kPackedSfixed32:
225         encoder
226             .WritePackedSfixed32(provider.ConsumeIntegral<uint32_t>(),
227                                  ConsumeSpan<int32_t>(provider, &s32s))
228             .IgnoreError();
229         break;
230       case kSfixed64:
231         encoder
232             .WriteSfixed64(provider.ConsumeIntegral<uint32_t>(),
233                            provider.ConsumeIntegral<int64_t>())
234             .IgnoreError();
235         break;
236       case kPackedSfixed64:
237         encoder
238             .WritePackedSfixed64(provider.ConsumeIntegral<uint32_t>(),
239                                  ConsumeSpan<int64_t>(provider, &s64s))
240             .IgnoreError();
241         break;
242       case kFloat:
243         encoder
244             .WriteFloat(provider.ConsumeIntegral<uint32_t>(),
245                         provider.ConsumeFloatingPoint<float>())
246             .IgnoreError();
247         break;
248       case kPackedFloat:
249         encoder
250             .WritePackedFloat(provider.ConsumeIntegral<uint32_t>(),
251                               ConsumeSpan<float>(provider, &floats))
252             .IgnoreError();
253         break;
254       case kDouble:
255         encoder
256             .WriteDouble(provider.ConsumeIntegral<uint32_t>(),
257                          provider.ConsumeFloatingPoint<double>())
258             .IgnoreError();
259         break;
260       case kPackedDouble:
261         encoder
262             .WritePackedDouble(provider.ConsumeIntegral<uint32_t>(),
263                                ConsumeSpan<double>(provider, &doubles))
264             .IgnoreError();
265         break;
266       case kBytes:
267         encoder
268             .WriteBytes(provider.ConsumeIntegral<uint32_t>(),
269                         ConsumeBytes(provider, &bytes))
270             .IgnoreError();
271         break;
272       case kString:
273         encoder
274             .WriteString(provider.ConsumeIntegral<uint32_t>(),
275                          ConsumeString(provider, &strings))
276             .IgnoreError();
277         break;
278       case kPush: {
279         // Special "field". The marks the start of a nested message.
280         StreamEncoder nested_encoder =
281             encoder.GetNestedEncoder(provider.ConsumeIntegral<uint32_t>());
282         RecursiveFuzzedEncode(provider, nested_encoder, depth + 1);
283         break;
284       }
285       case kPop:
286         if (depth > 0) {
287           // Special "field". The marks the end of a nested message.
288           return;
289         }
290     }
291   }
292 }
293 
TestOneInput(FuzzedDataProvider & provider)294 void TestOneInput(FuzzedDataProvider& provider) {
295   static std::byte buffer[65536];
296 
297   // Pick a subset of the buffer that the fuzzer is allowed to use, and poison
298   // the rest.
299   size_t unpoisoned_length =
300       provider.ConsumeIntegralInRange<size_t>(0, sizeof(buffer));
301   ByteSpan unpoisoned(buffer, unpoisoned_length);
302   void* poisoned = &buffer[unpoisoned_length];
303   size_t poisoned_length = sizeof(buffer) - unpoisoned_length;
304   ASAN_POISON_MEMORY_REGION(poisoned, poisoned_length);
305 
306   pw::protobuf::MemoryEncoder encoder(unpoisoned);
307   RecursiveFuzzedEncode(provider, encoder);
308 
309   // Don't forget to unpoison for the next iteration!
310   ASAN_UNPOISON_MEMORY_REGION(poisoned, poisoned_length);
311 }
312 
313 }  // namespace
314 }  // namespace pw::protobuf::fuzz
315 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)316 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
317   FuzzedDataProvider provider(data, size);
318   pw::protobuf::fuzz::TestOneInput(provider);
319   return 0;
320 }
321