// Copyright (C) 2020 The Android Open Source Project // // 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 // // http://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. #pragma once #include #include #include #include #include #include // Utilities for using a protobuf definition to fuzz APIs in a class. // Terms: // The "fuzzed class" is the C++ class definition whose functions are fuzzed. // The "fuzzed object" is an instantiated object of the fuzzed class. It is // typically created and destroyed for each test run. // An "action" is an operation on the fuzzed object that may mutate its state. // This typically involves one function call into the fuzzed object. namespace android::fuzz { // CHECK(value) << msg void CheckInternal(bool value, std::string_view msg); // Get the oneof descriptor inside Action const google::protobuf::OneofDescriptor* GetProtoValueDescriptor( const google::protobuf::Descriptor* action_desc); template using FunctionMapImpl = std::map>; template class FunctionMap : public FunctionMapImpl { public: void CheckEmplace(typename FunctionMapImpl::key_type key, typename FunctionMapImpl::mapped_type&& value) { auto [it, inserted] = this->emplace(key, std::move(value)); CheckInternal(inserted, "Multiple implementation registered for tag number " + std::to_string(key)); } }; template int CheckConsistency() { const auto* function_map = Action::GetFunctionMap(); const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor()); for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) { const auto* field_desc = action_value_desc->field(field_index); CheckInternal(function_map->find(field_desc->number()) != function_map->end(), "Missing impl for function " + field_desc->camelcase_name()); } return 0; } // Get the field descriptor for the oneof field in the action message. If no oneof field is set, // return nullptr. template const google::protobuf::FieldDescriptor* GetValueFieldDescriptor( const typename Action::Proto& action_proto) { static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor()); auto* action_refl = Action::Proto::GetReflection(); if (!action_refl->HasOneof(action_proto, action_value_desc)) { return nullptr; } return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc); } template void ExecuteActionProto(typename Action::ClassType* module, const typename Action::Proto& action_proto) { const auto* field_desc = GetValueFieldDescriptor(action_proto); if (field_desc == nullptr) return; auto number = field_desc->number(); const auto& map = *Action::GetFunctionMap(); auto it = map.find(number); CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name()); const auto& func = it->second; func(module, action_proto, field_desc); } template void ExecuteAllActionProtos( typename Action::ClassType* module, const google::protobuf::RepeatedPtrField& action_protos) { for (const auto& proto : action_protos) { ExecuteActionProto(module, proto); } } // Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr. template const T* SafeCast(const google::protobuf::Message& message) { if (message.GetDescriptor() != T::GetDescriptor()) { return nullptr; } return static_cast(&message); } // Cast message to const T&. Abort if type mismatch. template const T& CheckedCast(const google::protobuf::Message& message) { const auto* ptr = SafeCast(message); CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " + T::GetDescriptor()->name()); return *ptr; } // A templated way to a primitive field from a message using reflection. template struct PrimitiveGetter; #define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name) \ template <> \ struct PrimitiveGetter { \ static constexpr const auto fp = &google::protobuf::Reflection::func_name; \ } FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool); FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32); FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32); FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64); FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64); FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble); FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat); // ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction // with these arguments. template struct ActionPerformerImpl; // undefined template struct ActionPerformerImpl< FuzzFunction, void(const MessageProto&), typename std::enable_if_t>> { static typename FuzzFunction::ReturnType Invoke( typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto, const google::protobuf::FieldDescriptor* field_desc) { const MessageProto& arg = CheckedCast>( action_proto.GetReflection()->GetMessage(action_proto, field_desc)); return FuzzFunction::ImplBody(module, arg); } }; template struct ActionPerformerImpl>> { static typename FuzzFunction::ReturnType Invoke( typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto, const google::protobuf::FieldDescriptor* field_desc) { Primitive arg = std::invoke(PrimitiveGetter::fp, action_proto.GetReflection(), action_proto, field_desc); return FuzzFunction::ImplBody(module, arg); } }; template struct ActionPerformerImpl { static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module, const google::protobuf::Message&, const google::protobuf::FieldDescriptor*) { return FuzzFunction::ImplBody(module); } }; template struct ActionPerformerImpl { static typename FuzzFunction::ReturnType Invoke( typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto, const google::protobuf::FieldDescriptor* field_desc) { std::string scratch; const std::string& arg = action_proto.GetReflection()->GetStringReference( action_proto, field_desc, &scratch); return FuzzFunction::ImplBody(module, arg); } }; template struct ActionPerformer : ActionPerformerImpl {}; } // namespace android::fuzz // Fuzz existing C++ class, ClassType, with a collection of functions under the name Action. // // Prerequisite: ActionProto must be defined in Protobuf to describe possible actions: // message FooActionProto { // message NoArgs {} // oneof value { // bool do_foo = 1; // NoArgs do_bar = 1; // } // } // Use it to fuzz a C++ class Foo by doing the following: // FUZZ_CLASS(Foo, FooAction) // After linking functions of Foo to FooAction, execute all actions by: // FooAction::ExecuteAll(foo_object, action_protos) #define FUZZ_CLASS(Class, Action) \ class Action { \ public: \ using Proto = Action##Proto; \ using ClassType = Class; \ using FunctionMap = android::fuzz::FunctionMap; \ static FunctionMap* GetFunctionMap() { \ static Action::FunctionMap map; \ return ↦ \ } \ static void ExecuteAll(Class* module, \ const google::protobuf::RepeatedPtrField& action_protos) { \ [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency(); \ android::fuzz::ExecuteAllActionProtos(module, action_protos); \ } \ } #define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName #define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName // Implement an action defined in protobuf. Example: // message FooActionProto { // oneof value { // bool do_foo = 1; // } // } // class Foo { public: void DoAwesomeFoo(bool arg); }; // FUZZ_OBJECT(FooAction, Foo); // FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) { // module->DoAwesomeFoo(arg); // } // The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto. #define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...) \ class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \ public: \ using ActionType = Action; \ using ClassType = Action::ClassType; \ using ReturnType = Return; \ using Signature = void(__VA_ARGS__); \ static constexpr const char name[] = #FunctionName; \ static constexpr const auto tag = \ Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \ static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__); \ \ private: \ static bool registered_; \ }; \ auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \ auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag; \ auto func = &::android::fuzz::ActionPerformer::Invoke; \ Action::GetFunctionMap()->CheckEmplace(tag, func); \ return true; \ })(); \ Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__) // Implement a simple action by linking it to the function with the same name. Example: // message FooActionProto { // message NoArgs {} // oneof value { // NoArgs do_bar = 1; // } // } // class Foo { public void DoBar(); }; // FUZZ_OBJECT(FooAction, Foo); // FUZZ_FUNCTION(FooAction, DoBar); // The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and // also the name of the function of Foo. #define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \ FUZZ_FUNCTION(Action, FunctionName, \ decltype(std::declval().FunctionName()), \ Action::ClassType* module) { \ return module->FunctionName(); \ }