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, &params.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