1 /*
2 * Copyright 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "ProtoFuzzerUtils.h"
18
19 #include <android-base/strings.h>
20 #include <dirent.h>
21 #include <getopt.h>
22 #include <algorithm>
23
24 #include "utils/InterfaceSpecUtil.h"
25
26 using android::base::Split;
27 using std::cerr;
28 using std::cout;
29 using std::string;
30 using std::unordered_map;
31 using std::vector;
32
33 namespace android {
34 namespace vts {
35 namespace fuzzer {
36
usage()37 static void usage() {
38 cout
39 << "Usage:\n"
40 "\n"
41 "./vts_proto_fuzzer <vts flags> -- <libfuzzer flags>\n"
42 "\n"
43 "VTS flags (strictly in form --flag=value):\n"
44 "\n"
45 "\tvts_binder_mode: if set, fuzzer will open the HAL in binder mode.\n"
46 "\tvts_exec_size: number of function calls per 1 run of "
47 "LLVMFuzzerTestOneInput.\n"
48 "\tvts_spec_dir: \":\"-separated list of directories on the target "
49 "containing .vts spec files.\n"
50 "\tvts_target_fq_name: fully-qualified name of interface targeted for "
51 "fuzz version, e.g. "
52 "\"android.hardware.nfc@1.1::INfc\".\n"
53 "\tvts_seed: optional integral argument used to initalize the random "
54 "number generator.\n"
55 "\n"
56 "libfuzzer flags (strictly in form -flag=value):\n"
57 "\tUse -help=1 to see libfuzzer flags\n"
58 "\n";
59 }
60
61 static struct option long_options[] = {
62 {"help", no_argument, 0, 'h'},
63 {"vts_binder_mode", no_argument, 0, 'b'},
64 {"vts_spec_dir", required_argument, 0, 'd'},
65 {"vts_exec_size", required_argument, 0, 'e'},
66 {"vts_seed", required_argument, 0, 's'},
67 {"vts_target_fq_name", required_argument, 0, 't'}};
68
69 // Removes information from CompSpec not needed by fuzzer.
TrimCompSpec(CompSpec * comp_spec)70 static void TrimCompSpec(CompSpec *comp_spec) {
71 if (comp_spec == nullptr) {
72 cerr << __func__ << ": empty CompSpec." << endl;
73 return;
74 }
75 if (comp_spec->has_interface()) {
76 auto *iface_spec = comp_spec->mutable_interface();
77 for (auto i = 0; i < iface_spec->api_size(); ++i) {
78 iface_spec->mutable_api(i)->clear_callflow();
79 }
80 }
81 }
82
ExtractCompSpecs(const vector<string> & dirs)83 vector<CompSpec> ExtractCompSpecs(const vector<string> &dirs) {
84 vector<CompSpec> result{};
85
86 for (const auto &dir_path : dirs) {
87 DIR *dir;
88 struct dirent *ent;
89 if (!(dir = opendir(dir_path.c_str()))) {
90 cerr << "Could not open directory: " << dir_path << endl;
91 std::abort();
92 }
93 while ((ent = readdir(dir))) {
94 string vts_spec_name{ent->d_name};
95 if (vts_spec_name.find(".vts") != string::npos) {
96 string vts_spec_path = dir_path + "/" + vts_spec_name;
97 CompSpec comp_spec{};
98 ParseInterfaceSpec(vts_spec_path.c_str(), &comp_spec);
99 TrimCompSpec(&comp_spec);
100 result.emplace_back(std::move(comp_spec));
101 }
102 }
103 }
104 return result;
105 }
106
ExtractPredefinedTypesFromVar(const TypeSpec & var_spec,unordered_map<string,TypeSpec> & predefined_types)107 static void ExtractPredefinedTypesFromVar(
108 const TypeSpec &var_spec,
109 unordered_map<string, TypeSpec> &predefined_types) {
110 predefined_types[var_spec.name()] = var_spec;
111 // Find all nested struct declarations.
112 for (const auto &sub_var_spec : var_spec.sub_struct()) {
113 ExtractPredefinedTypesFromVar(sub_var_spec, predefined_types);
114 }
115 // Find all nested union declarations.
116 for (const auto &sub_var_spec : var_spec.sub_union()) {
117 ExtractPredefinedTypesFromVar(sub_var_spec, predefined_types);
118 }
119 }
120
ExtractProtoFuzzerParams(int argc,char ** argv)121 ProtoFuzzerParams ExtractProtoFuzzerParams(int argc, char **argv) {
122 ProtoFuzzerParams params;
123 int opt = 0;
124 int index = 0;
125 while ((opt = getopt_long_only(argc, argv, "", long_options, &index)) != -1) {
126 switch (opt) {
127 case 'h':
128 usage();
129 std::abort();
130 case 'b':
131 params.binder_mode_ = true;
132 break;
133 case 'd':
134 // optarg is a column-separated list of directories
135 params.comp_specs_ = ExtractCompSpecs(Split(optarg, ":"));
136 break;
137 case 'e':
138 params.exec_size_ = std::stoul(optarg);
139 break;
140 case 's':
141 params.seed_ = std::stoull(optarg);
142 break;
143 case 't': {
144 if (!FQName::parse(optarg, ¶ms.target_fq_name_)) {
145 usage();
146 std::abort();
147 }
148 break;
149 }
150 default:
151 // Ignore. This option will be handled by libfuzzer.
152 break;
153 }
154 }
155 return params;
156 }
157
DebugString() const158 string ProtoFuzzerParams::DebugString() const {
159 std::stringstream ss;
160 ss << "Execution size: " << exec_size_ << endl;
161 ss << "Target FQ name: " << target_fq_name_.string() << endl;
162 ss << "Binder mode: " << binder_mode_ << endl;
163 ss << "Seed: " << seed_ << endl;
164 ss << "Loaded specs: " << endl;
165 for (const auto &spec : comp_specs_) {
166 ss << spec.component_name() << endl;
167 }
168 return ss.str();
169 }
170
ExtractPredefinedTypes(const vector<CompSpec> & specs)171 unordered_map<string, TypeSpec> ExtractPredefinedTypes(
172 const vector<CompSpec> &specs) {
173 unordered_map<string, TypeSpec> predefined_types;
174 for (const auto &comp_spec : specs) {
175 for (const auto &var_spec : comp_spec.attribute()) {
176 ExtractPredefinedTypesFromVar(var_spec, predefined_types);
177 }
178 for (const auto &var_spec : comp_spec.interface().attribute()) {
179 ExtractPredefinedTypesFromVar(var_spec, predefined_types);
180 }
181 }
182 return predefined_types;
183 }
184
FromArray(const uint8_t * data,size_t size,ExecSpec * exec_spec)185 bool FromArray(const uint8_t *data, size_t size, ExecSpec *exec_spec) {
186 // TODO(b/63136690): Use checksum to validate exec_spec more reliably.
187 return exec_spec->ParseFromArray(data, size) && exec_spec->has_valid() &&
188 exec_spec->valid();
189 }
190
ToArray(uint8_t * data,size_t size,ExecSpec * exec_spec)191 size_t ToArray(uint8_t *data, size_t size, ExecSpec *exec_spec) {
192 exec_spec->set_valid(true);
193 size_t exec_size = exec_spec->ByteSize();
194 exec_spec->SerializeToArray(data, exec_size);
195 return exec_size;
196 }
197
198 } // namespace fuzzer
199 } // namespace vts
200 } // namespace android
201