// Copyright 2019 The Pigweed Authors // // 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 // // https://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 #include #include #include #include #include "pw_fuzzer/asan_interface.h" #include "pw_fuzzer/fuzzed_data_provider.h" #include "pw_protobuf/encoder.h" namespace { // Encodable values. The fuzzer will iteratively choose different field types to // generate and encode. enum FieldType : uint8_t { kUint32 = 0, kPackedUint32, kUint64, kPackedUint64, kInt32, kPackedInt32, kInt64, kPackedInt64, kSint32, kPackedSint32, kSint64, kPackedSint64, kBool, kFixed32, kPackedFixed32, kFixed64, kPackedFixed64, kSfixed32, kPackedSfixed32, kSfixed64, kPackedSfixed64, kFloat, kPackedFloat, kDouble, kPackedDouble, kBytes, kString, kPush, kMaxValue = kPush, }; // TODO(pwbug/181): Move this to pw_fuzzer/fuzzed_data_provider.h // Uses the given |provider| to pick and return a number between 0 and the // maximum numbers of T that can be generated from the remaining input data. template size_t ConsumeSize(FuzzedDataProvider* provider) { size_t max = provider->remaining_bytes() / sizeof(T); return provider->ConsumeIntegralInRange(0, max); } // Uses the given |provider| to generate several instances of T, store them in // |data|, and then return a std::span to them. It is the caller's responsbility // to ensure |data| remains in scope as long as the returned std::span. template std::span ConsumeSpan(FuzzedDataProvider* provider, std::vector* data) { size_t num = ConsumeSize(provider); size_t off = data->size(); data->reserve(off + num); for (size_t i = 0; i < num; ++i) { if constexpr (std::is_floating_point::value) { data->push_back(provider->ConsumeFloatingPoint()); } else { data->push_back(provider->ConsumeIntegral()); } } return std::span(&((*data)[off]), num); } // Uses the given |provider| to generate a string, store it in |data|, and // return a C-style representation. It is the caller's responsbility to // ensure |data| remains in scope as long as the returned char*. const char* ConsumeString(FuzzedDataProvider* provider, std::vector* data) { size_t off = data->size(); // OSS-Fuzz's clang doesn't have the zero-parameter version of // ConsumeRandomLengthString yet. size_t max_length = std::numeric_limits::max(); data->push_back(provider->ConsumeRandomLengthString(max_length)); return (*data)[off].c_str(); } // Uses the given |provider| to generate non-arithmetic bytes, store them in // |data|, and return a std::span to them. It is the caller's responsbility to // ensure |data| remains in scope as long as the returned std::span. std::span ConsumeBytes(FuzzedDataProvider* provider, std::vector* data) { size_t num = ConsumeSize(provider); auto added = provider->ConsumeBytes(num); size_t off = data->size(); num = added.size(); data->insert(data->end(), added.begin(), added.end()); return std::span(&((*data)[off]), num); } } // namespace extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static std::byte buffer[65536]; FuzzedDataProvider provider(data, size); // Pick a subset of the buffer that the fuzzer is allowed to use, and poison // the rest. size_t unpoisoned_length = provider.ConsumeIntegralInRange(0, sizeof(buffer)); std::span unpoisoned(buffer, unpoisoned_length); void* poisoned = &buffer[unpoisoned_length]; size_t poisoned_length = sizeof(buffer) - unpoisoned_length; ASAN_POISON_MEMORY_REGION(poisoned, poisoned_length); pw::protobuf::MemoryEncoder encoder(unpoisoned); // Storage for generated spans std::vector u32s; std::vector u64s; std::vector s32s; std::vector s64s; std::vector floats; std::vector doubles; std::vector strings; std::vector bytes; // Consume the fuzzing input, using it to generate a sequence of fields to // encode. Both the uint32_t field IDs and the fields values are generated. // Don't try to detect errors, ensures pushes and pops are balanced, or // otherwise hold the interface correctly. Instead, fuzz the widest possbile // set of inputs to the encoder to ensure it doesn't misbehave. while (provider.remaining_bytes() != 0) { switch (provider.ConsumeEnum()) { case kUint32: encoder .WriteUint32(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedUint32: encoder .WritePackedUint32(provider.ConsumeIntegral(), ConsumeSpan(&provider, &u32s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kUint64: encoder .WriteUint64(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedUint64: encoder .WritePackedUint64(provider.ConsumeIntegral(), ConsumeSpan(&provider, &u64s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kInt32: encoder .WriteInt32(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedInt32: encoder .WritePackedInt32(provider.ConsumeIntegral(), ConsumeSpan(&provider, &s32s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kInt64: encoder .WriteInt64(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedInt64: encoder .WritePackedInt64(provider.ConsumeIntegral(), ConsumeSpan(&provider, &s64s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kSint32: encoder .WriteSint32(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedSint32: encoder .WritePackedSint32(provider.ConsumeIntegral(), ConsumeSpan(&provider, &s32s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kSint64: encoder .WriteSint64(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedSint64: encoder .WritePackedSint64(provider.ConsumeIntegral(), ConsumeSpan(&provider, &s64s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kBool: encoder .WriteBool(provider.ConsumeIntegral(), provider.ConsumeBool()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kFixed32: encoder .WriteFixed32(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedFixed32: encoder .WritePackedFixed32(provider.ConsumeIntegral(), ConsumeSpan(&provider, &u32s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kFixed64: encoder .WriteFixed64(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedFixed64: encoder .WritePackedFixed64(provider.ConsumeIntegral(), ConsumeSpan(&provider, &u64s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kSfixed32: encoder .WriteSfixed32(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedSfixed32: encoder .WritePackedSfixed32(provider.ConsumeIntegral(), ConsumeSpan(&provider, &s32s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kSfixed64: encoder .WriteSfixed64(provider.ConsumeIntegral(), provider.ConsumeIntegral()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedSfixed64: encoder .WritePackedSfixed64(provider.ConsumeIntegral(), ConsumeSpan(&provider, &s64s)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kFloat: encoder .WriteFloat(provider.ConsumeIntegral(), provider.ConsumeFloatingPoint()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedFloat: encoder .WritePackedFloat(provider.ConsumeIntegral(), ConsumeSpan(&provider, &floats)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kDouble: encoder .WriteDouble(provider.ConsumeIntegral(), provider.ConsumeFloatingPoint()) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPackedDouble: encoder .WritePackedDouble(provider.ConsumeIntegral(), ConsumeSpan(&provider, &doubles)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kBytes: encoder .WriteBytes(provider.ConsumeIntegral(), ConsumeBytes(&provider, &bytes)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kString: encoder .WriteString(provider.ConsumeIntegral(), ConsumeString(&provider, &strings)) .IgnoreError(); // TODO(pwbug/387): Handle Status properly break; case kPush: // Special "field". The marks the start of a nested message. encoder.GetNestedEncoder(provider.ConsumeIntegral()); break; } } // Don't forget to unpoison for the next iteration! ASAN_UNPOISON_MEMORY_REGION(poisoned, poisoned_length); return 0; }