• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include <map>
18 #include <string>
19 #include <string_view>
20 
21 #include <google/protobuf/descriptor.h>
22 #include <google/protobuf/message.h>
23 #include <google/protobuf/repeated_field.h>
24 
25 // Utilities for using a protobuf definition to fuzz APIs in a class.
26 // Terms:
27 // The "fuzzed class" is the C++ class definition whose functions are fuzzed.
28 // The "fuzzed object" is an instantiated object of the fuzzed class. It is
29 //   typically created and destroyed for each test run.
30 // An "action" is an operation on the fuzzed object that may mutate its state.
31 //   This typically involves one function call into the fuzzed object.
32 
33 namespace android::fuzz {
34 
35 // CHECK(value) << msg
36 void CheckInternal(bool value, std::string_view msg);
37 
38 // Get the oneof descriptor inside Action
39 const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
40         const google::protobuf::Descriptor* action_desc);
41 
42 template <typename Class>
43 using FunctionMapImpl =
44         std::map<int, std::function<void(Class*, const google::protobuf::Message& action_proto,
45                                          const google::protobuf::FieldDescriptor* field_desc)>>;
46 
47 template <typename Class>
48 class FunctionMap : public FunctionMapImpl<Class> {
49   public:
CheckEmplace(typename FunctionMapImpl<Class>::key_type key,typename FunctionMapImpl<Class>::mapped_type && value)50     void CheckEmplace(typename FunctionMapImpl<Class>::key_type key,
51                       typename FunctionMapImpl<Class>::mapped_type&& value) {
52         auto [it, inserted] = this->emplace(key, std::move(value));
53         CheckInternal(inserted,
54                       "Multiple implementation registered for tag number " + std::to_string(key));
55     }
56 };
57 
58 template <typename Action>
CheckConsistency()59 int CheckConsistency() {
60     const auto* function_map = Action::GetFunctionMap();
61     const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
62 
63     for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) {
64         const auto* field_desc = action_value_desc->field(field_index);
65         CheckInternal(function_map->find(field_desc->number()) != function_map->end(),
66                       "Missing impl for function " + field_desc->camelcase_name());
67     }
68     return 0;
69 }
70 
71 // Get the field descriptor for the oneof field in the action message. If no oneof field is set,
72 // return nullptr.
73 template <typename Action>
GetValueFieldDescriptor(const typename Action::Proto & action_proto)74 const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
75         const typename Action::Proto& action_proto) {
76     static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
77 
78     auto* action_refl = Action::Proto::GetReflection();
79     if (!action_refl->HasOneof(action_proto, action_value_desc)) {
80         return nullptr;
81     }
82     return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
83 }
84 
85 template <typename Action>
ExecuteActionProto(typename Action::ClassType * module,const typename Action::Proto & action_proto)86 void ExecuteActionProto(typename Action::ClassType* module,
87                         const typename Action::Proto& action_proto) {
88     const auto* field_desc = GetValueFieldDescriptor<Action>(action_proto);
89     if (field_desc == nullptr) return;
90     auto number = field_desc->number();
91     const auto& map = *Action::GetFunctionMap();
92     auto it = map.find(number);
93     CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name());
94     const auto& func = it->second;
95     func(module, action_proto, field_desc);
96 }
97 
98 template <typename Action>
ExecuteAllActionProtos(typename Action::ClassType * module,const google::protobuf::RepeatedPtrField<typename Action::Proto> & action_protos)99 void ExecuteAllActionProtos(
100         typename Action::ClassType* module,
101         const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
102     for (const auto& proto : action_protos) {
103         ExecuteActionProto<Action>(module, proto);
104     }
105 }
106 
107 // Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr.
108 template <typename T>
SafeCast(const google::protobuf::Message & message)109 const T* SafeCast(const google::protobuf::Message& message) {
110     if (message.GetDescriptor() != T::GetDescriptor()) {
111         return nullptr;
112     }
113     return static_cast<const T*>(&message);
114 }
115 
116 // Cast message to const T&. Abort if type mismatch.
117 template <typename T>
CheckedCast(const google::protobuf::Message & message)118 const T& CheckedCast(const google::protobuf::Message& message) {
119     const auto* ptr = SafeCast<T>(message);
120     CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " +
121                                T::GetDescriptor()->name());
122     return *ptr;
123 }
124 
125 // A templated way to a primitive field from a message using reflection.
126 template <typename T>
127 struct PrimitiveGetter;
128 #define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name)                              \
129     template <>                                                                    \
130     struct PrimitiveGetter<type> {                                                 \
131         static constexpr const auto fp = &google::protobuf::Reflection::func_name; \
132     }
133 
134 FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool);
135 FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32);
136 FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32);
137 FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64);
138 FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64);
139 FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble);
140 FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat);
141 
142 // ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
143 // with these arguments.
144 template <typename FuzzFunction, typename Signature, typename Enabled = void>
145 struct ActionPerformerImpl;  // undefined
146 
147 template <typename FuzzFunction, typename MessageProto>
148 struct ActionPerformerImpl<
149         FuzzFunction, void(const MessageProto&),
150         typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
151     static typename FuzzFunction::ReturnType Invoke(
152             typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
153             const google::protobuf::FieldDescriptor* field_desc) {
154         const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
155                 action_proto.GetReflection()->GetMessage(action_proto, field_desc));
156         return FuzzFunction::ImplBody(module, arg);
157     }
158 };
159 
160 template <typename FuzzFunction, typename Primitive>
161 struct ActionPerformerImpl<FuzzFunction, void(Primitive),
162                            typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
163     static typename FuzzFunction::ReturnType Invoke(
164             typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
165             const google::protobuf::FieldDescriptor* field_desc) {
166         Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
167                                     action_proto, field_desc);
168         return FuzzFunction::ImplBody(module, arg);
169     }
170 };
171 
172 template <typename FuzzFunction>
173 struct ActionPerformerImpl<FuzzFunction, void()> {
174     static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
175                                                     const google::protobuf::Message&,
176                                                     const google::protobuf::FieldDescriptor*) {
177         return FuzzFunction::ImplBody(module);
178     }
179 };
180 
181 template <typename FuzzFunction>
182 struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
183     static typename FuzzFunction::ReturnType Invoke(
184             typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
185             const google::protobuf::FieldDescriptor* field_desc) {
186         std::string scratch;
187         const std::string& arg = action_proto.GetReflection()->GetStringReference(
188                 action_proto, field_desc, &scratch);
189         return FuzzFunction::ImplBody(module, arg);
190     }
191 };
192 
193 template <typename FuzzFunction>
194 struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
195 
196 }  // namespace android::fuzz
197 
198 // Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
199 //
200 // Prerequisite: ActionProto must be defined in Protobuf to describe possible actions:
201 // message FooActionProto {
202 //     message NoArgs {}
203 //     oneof value {
204 //         bool do_foo = 1;
205 //         NoArgs do_bar = 1;
206 //     }
207 // }
208 // Use it to fuzz a C++ class Foo by doing the following:
209 //   FUZZ_CLASS(Foo, FooAction)
210 // After linking functions of Foo to FooAction, execute all actions by:
211 //   FooAction::ExecuteAll(foo_object, action_protos)
212 #define FUZZ_CLASS(Class, Action)                                                                \
213     class Action {                                                                               \
214       public:                                                                                    \
215         using Proto = Action##Proto;                                                             \
216         using ClassType = Class;                                                                 \
217         using FunctionMap = android::fuzz::FunctionMap<Class>;                                   \
218         static FunctionMap* GetFunctionMap() {                                                   \
219             static Action::FunctionMap map;                                                      \
220             return &map;                                                                         \
221         }                                                                                        \
222         static void ExecuteAll(Class* module,                                                    \
223                                const google::protobuf::RepeatedPtrField<Proto>& action_protos) { \
224             [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency<Action>();  \
225             android::fuzz::ExecuteAllActionProtos<Action>(module, action_protos);                \
226         }                                                                                        \
227     }
228 
229 #define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName
230 #define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName
231 
232 // Implement an action defined in protobuf. Example:
233 // message FooActionProto {
234 //     oneof value {
235 //         bool do_foo = 1;
236 //     }
237 // }
238 // class Foo { public: void DoAwesomeFoo(bool arg); };
239 // FUZZ_OBJECT(FooAction, Foo);
240 // FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
241 //   module->DoAwesomeFoo(arg);
242 // }
243 // The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
244 #define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...)             \
245     class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) {                      \
246       public:                                                                   \
247         using ActionType = Action;                                              \
248         using ClassType = Action::ClassType;                                    \
249         using ReturnType = Return;                                              \
250         using Signature = void(__VA_ARGS__);                                    \
251         static constexpr const char name[] = #FunctionName;                     \
252         static constexpr const auto tag =                                       \
253                 Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
254         static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__);                   \
255                                                                                 \
256       private:                                                                  \
257         static bool registered_;                                                \
258     };                                                                          \
259     auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] {    \
260         auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag;         \
261         auto func = &::android::fuzz::ActionPerformer<FUZZ_FUNCTION_CLASS_NAME( \
262                 Action, FunctionName)>::Invoke;                                 \
263         Action::GetFunctionMap()->CheckEmplace(tag, func);                      \
264         return true;                                                            \
265     })();                                                                       \
266     Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__)
267 
268 // Implement a simple action by linking it to the function with the same name. Example:
269 // message FooActionProto {
270 //     message NoArgs {}
271 //     oneof value {
272 //         NoArgs do_bar = 1;
273 //     }
274 // }
275 // class Foo { public void DoBar(); };
276 // FUZZ_OBJECT(FooAction, Foo);
277 // FUZZ_FUNCTION(FooAction, DoBar);
278 // The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
279 // also the name of the function of Foo.
280 #define FUZZ_SIMPLE_FUNCTION(Action, FunctionName)                            \
281     FUZZ_FUNCTION(Action, FunctionName,                                       \
282                   decltype(std::declval<Action::ClassType>().FunctionName()), \
283                   Action::ClassType* module) {                                \
284         return module->FunctionName();                                        \
285     }
286