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 ↦ \
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