// Copyright 2019 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr int kRandomMaxContainerSize = 8; constexpr int kRandomMaxDataLength = 128; typedef enum DataType { kUint8 = 0, kUint16, kUint32, kUint64, kInt16, kInt32, kInt64, kBool, kDouble, kString, kObjectPath, // A couple vector types. kVectorInt16, kVectorString, // A couple pair types. kPairBoolInt64, kPairUint32String, // A couple tuple types. kTupleUint16StringBool, kTupleDoubleInt32ObjectPath, // A couple map types. kMapInt32String, kMapDoubleBool, kMaxValue = kMapDoubleBool, } DataType; template void AppendValue(dbus::MessageWriter* writer, bool variant, const T& value) { if (variant) brillo::dbus_utils::AppendValueToWriterAsVariant(writer, value); else brillo::dbus_utils::AppendValueToWriter(writer, value); } template void GenerateIntAndAppendValue(FuzzedDataProvider* data_provider, dbus::MessageWriter* writer, bool variant) { AppendValue(writer, variant, data_provider->ConsumeIntegral()); } template void PopValue(dbus::MessageReader* reader, bool variant, T* value) { if (variant) brillo::dbus_utils::PopVariantValueFromReader(reader, value); else brillo::dbus_utils::PopValueFromReader(reader, value); } std::string GenerateValidUTF8(FuzzedDataProvider* data_provider) { // >= 0x80 // Generates a random string and returns it if it is valid UTF8, if it is not // then it will strip it down to all the 7-bit ASCII chars and just return // that string. std::string str = data_provider->ConsumeRandomLengthString(kRandomMaxDataLength); if (base::IsStringUTF8(str)) return str; for (auto it = str.begin(); it != str.end(); it++) { if (static_cast(*it) >= 0x80) { // Might be invalid, remove it. it = str.erase(it); it--; } } return str; } } // namespace class Environment { public: Environment() { // Disable logging. logging::SetMinLogLevel(logging::LOG_FATAL); } }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static Environment env; FuzzedDataProvider data_provider(data, size); // Consume a random fraction of our data writing random things to a D-Bus // message, and then consume the remaining data reading randomly from that // same D-Bus message. Given the templated nature of these functions and that // they support essentially an infinite amount of types, we are constraining // this to a fixed set of types defined above. std::unique_ptr message = dbus::Response::CreateEmpty(); dbus::MessageWriter writer(message.get()); int bytes_left_for_read = static_cast(data_provider.ConsumeProbability() * size); while (data_provider.remaining_bytes() > bytes_left_for_read) { DataType curr_type = data_provider.ConsumeEnum(); bool variant = data_provider.ConsumeBool(); switch (curr_type) { case kUint8: GenerateIntAndAppendValue(&data_provider, &writer, variant); break; case kUint16: GenerateIntAndAppendValue(&data_provider, &writer, variant); break; case kUint32: GenerateIntAndAppendValue(&data_provider, &writer, variant); break; case kUint64: GenerateIntAndAppendValue(&data_provider, &writer, variant); break; case kInt16: GenerateIntAndAppendValue(&data_provider, &writer, variant); break; case kInt32: GenerateIntAndAppendValue(&data_provider, &writer, variant); break; case kInt64: GenerateIntAndAppendValue(&data_provider, &writer, variant); break; case kBool: AppendValue(&writer, variant, data_provider.ConsumeBool()); break; case kDouble: AppendValue(&writer, variant, data_provider.ConsumeProbability()); break; case kString: AppendValue(&writer, variant, GenerateValidUTF8(&data_provider)); break; case kObjectPath: { std::string object_path = data_provider.ConsumeRandomLengthString(kRandomMaxDataLength); // If this isn't valid we'll hit a CHECK failure. if (dbus::IsValidObjectPath(object_path)) AppendValue(&writer, variant, dbus::ObjectPath(object_path)); break; } case kVectorInt16: { int vec_size = data_provider.ConsumeIntegralInRange( 0, kRandomMaxContainerSize); std::vector vec(vec_size); for (int i = 0; i < vec_size; i++) vec[i] = data_provider.ConsumeIntegral(); AppendValue(&writer, variant, vec); break; } case kVectorString: { int vec_size = data_provider.ConsumeIntegralInRange( 0, kRandomMaxContainerSize); std::vector vec(vec_size); for (int i = 0; i < vec_size; i++) vec[i] = GenerateValidUTF8(&data_provider); AppendValue(&writer, variant, vec); break; } case kPairBoolInt64: AppendValue( &writer, variant, std::pair{data_provider.ConsumeBool(), data_provider.ConsumeIntegral()}); break; case kPairUint32String: AppendValue(&writer, variant, std::pair{ data_provider.ConsumeIntegral(), GenerateValidUTF8(&data_provider)}); break; case kTupleUint16StringBool: AppendValue(&writer, variant, std::tuple{ data_provider.ConsumeIntegral(), GenerateValidUTF8(&data_provider), data_provider.ConsumeBool()}); break; case kTupleDoubleInt32ObjectPath: { std::string object_path = data_provider.ConsumeRandomLengthString(kRandomMaxDataLength); // If this isn't valid we'll hit a CHECK failure. if (dbus::IsValidObjectPath(object_path)) { AppendValue(&writer, variant, std::tuple{ data_provider.ConsumeProbability(), data_provider.ConsumeIntegral(), dbus::ObjectPath(object_path)}); } break; } case kMapInt32String: { int map_size = data_provider.ConsumeIntegralInRange( 0, kRandomMaxContainerSize); std::map map; for (int i = 0; i < map_size; i++) map[data_provider.ConsumeIntegral()] = GenerateValidUTF8(&data_provider); AppendValue(&writer, variant, map); break; } case kMapDoubleBool: { int map_size = data_provider.ConsumeIntegralInRange( 0, kRandomMaxContainerSize); std::map map; for (int i = 0; i < map_size; i++) map[data_provider.ConsumeProbability()] = data_provider.ConsumeBool(); AppendValue(&writer, variant, map); break; } } } dbus::MessageReader reader(message.get()); while (data_provider.remaining_bytes()) { DataType curr_type = data_provider.ConsumeEnum(); bool variant = data_provider.ConsumeBool(); switch (curr_type) { case kUint8: { uint8_t value; PopValue(&reader, variant, &value); break; } case kUint16: { uint16_t value; PopValue(&reader, variant, &value); break; } case kUint32: { uint32_t value; PopValue(&reader, variant, &value); break; } case kUint64: { uint64_t value; PopValue(&reader, variant, &value); break; } case kInt16: { int16_t value; PopValue(&reader, variant, &value); break; } case kInt32: { int32_t value; PopValue(&reader, variant, &value); break; } case kInt64: { int64_t value; PopValue(&reader, variant, &value); break; } case kBool: { bool value; PopValue(&reader, variant, &value); break; } case kDouble: { double value; PopValue(&reader, variant, &value); break; } case kString: { std::string value; PopValue(&reader, variant, &value); break; } case kObjectPath: { dbus::ObjectPath value; PopValue(&reader, variant, &value); break; } case kVectorInt16: { std::vector value; PopValue(&reader, variant, &value); break; } case kVectorString: { std::vector value; PopValue(&reader, variant, &value); break; } case kPairBoolInt64: { std::pair value; PopValue(&reader, variant, &value); break; } case kPairUint32String: { std::pair value; PopValue(&reader, variant, &value); break; } case kTupleUint16StringBool: { std::tuple value; break; } case kTupleDoubleInt32ObjectPath: { std::tuple value; PopValue(&reader, variant, &value); break; } case kMapInt32String: { std::map value; PopValue(&reader, variant, &value); break; } case kMapDoubleBool: { std::map value; PopValue(&reader, variant, &value); break; } } } return 0; }