// Copyright 2022 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 "fuzz.h" #include "pw_fuzzer/fuzzed_data_provider.h" #include "pw_protobuf/stream_decoder.h" #include "pw_span/span.h" #include "pw_status/status.h" #include "pw_status/status_with_size.h" #include "pw_stream/memory_stream.h" #include "pw_stream/stream.h" namespace pw::protobuf::fuzz { namespace { void RecursiveFuzzedDecode(FuzzedDataProvider& provider, StreamDecoder& decoder, uint32_t depth = 0) { constexpr size_t kMaxRepeatedRead = 256; constexpr size_t kMaxDepth = 3; if (depth > kMaxDepth) { return; } while (provider.remaining_bytes() != 0 && decoder.Next().ok()) { FieldType field_type = provider.ConsumeEnum(); switch (field_type) { case kUint32: if (!decoder.ReadUint32().status().ok()) { return; } break; case kPackedUint32: { uint32_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedUint32(packed).status().ok()) { return; } } break; case kUint64: if (!decoder.ReadUint64().status().ok()) { return; } break; case kPackedUint64: { uint64_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedUint64(packed).status().ok()) { return; } } break; case kInt32: if (!decoder.ReadInt32().status().ok()) { return; } break; case kPackedInt32: { int32_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedInt32(packed).status().ok()) { return; } } break; case kInt64: if (!decoder.ReadInt64().status().ok()) { return; } break; case kPackedInt64: { int64_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedInt64(packed).status().ok()) { return; } } break; case kSint32: if (!decoder.ReadSint32().status().ok()) { return; } break; case kPackedSint32: { int32_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedSint32(packed).status().ok()) { return; } } break; case kSint64: if (!decoder.ReadSint64().status().ok()) { return; } break; case kPackedSint64: { int64_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedSint64(packed).status().ok()) { return; } } break; case kBool: if (!decoder.ReadBool().status().ok()) { return; } break; case kFixed32: if (!decoder.ReadFixed32().status().ok()) { return; } break; case kPackedFixed32: { uint32_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedFixed32(packed).status().ok()) { return; } } break; case kFixed64: if (!decoder.ReadFixed64().status().ok()) { return; } break; case kPackedFixed64: { uint64_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedFixed64(packed).status().ok()) { return; } } break; case kSfixed32: if (!decoder.ReadSfixed32().status().ok()) { return; } break; case kPackedSfixed32: { int32_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedSfixed32(packed).status().ok()) { return; } } break; case kSfixed64: if (!decoder.ReadSfixed64().status().ok()) { return; } break; case kPackedSfixed64: { int64_t packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedSfixed64(packed).status().ok()) { return; } } break; case kFloat: if (!decoder.ReadFloat().status().ok()) { return; } break; case kPackedFloat: { float packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedFloat(packed).status().ok()) { return; } } break; case kDouble: if (!decoder.ReadDouble().status().ok()) { return; } break; case kPackedDouble: { double packed[kMaxRepeatedRead] = {0}; if (!decoder.ReadPackedDouble(packed).status().ok()) { return; } } break; case kBytes: { std::byte bytes[kMaxRepeatedRead] = {std::byte{0}}; if (!decoder.ReadBytes(bytes).status().ok()) { return; } } break; case kString: { char str[kMaxRepeatedRead] = {0}; if (!decoder.ReadString(str).status().ok()) { return; } } break; case kPush: { StreamDecoder nested_decoder = decoder.GetNestedDecoder(); RecursiveFuzzedDecode(provider, nested_decoder, depth + 1); } break; case kPop: if (depth > 0) { // Special "field". The marks the end of a nested message. return; } } } } void TestOneInput(FuzzedDataProvider& provider) { constexpr size_t kMaxFuzzedProtoSize = 4096; std::vector proto_message_data = provider.ConsumeBytes( provider.ConsumeIntegralInRange(0, kMaxFuzzedProtoSize)); stream::MemoryReader memory_reader(proto_message_data); StreamDecoder decoder(memory_reader); RecursiveFuzzedDecode(provider, decoder); } } // namespace } // namespace pw::protobuf::fuzz extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider provider(data, size); pw::protobuf::fuzz::TestOneInput(provider); return 0; }