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 <dirent.h> 20 #include <getopt.h> 21 #include <algorithm> 22 #include <sstream> 23 24 #include "utils/InterfaceSpecUtil.h" 25 26 using std::cerr; 27 using std::cout; 28 using std::string; 29 using std::unordered_map; 30 using std::vector; 31 32 namespace android { 33 namespace vts { 34 namespace fuzzer { 35 36 static void usage() { 37 cout 38 << "Usage:\n" 39 "\n" 40 "./vts_proto_fuzzer <vts flags> -- <libfuzzer flags>\n" 41 "\n" 42 "VTS flags (strictly in form --flag=value):\n" 43 "\n" 44 "\tvts_binder_mode: if set, fuzzer will open the HAL in binder mode.\n" 45 "\tvts_exec_size: number of function calls per 1 run of " 46 "LLVMFuzzerTestOneInput.\n" 47 "\tvts_spec_dir: \":\"-separated list of directories on the target " 48 "containing .vts spec files.\n" 49 "\tvts_target_iface: name of interface targeted for fuzz, e.g. " 50 "\"INfc\".\n" 51 "\tvts_seed: optional integral argument used to initalize the random " 52 "number generator.\n" 53 "\n" 54 "libfuzzer flags (strictly in form -flag=value):\n" 55 "\tUse -help=1 to see libfuzzer flags\n" 56 "\n"; 57 } 58 59 static struct option long_options[] = { 60 {"help", no_argument, 0, 'h'}, 61 {"vts_binder_mode", no_argument, 0, 'b'}, 62 {"vts_spec_dir", required_argument, 0, 'd'}, 63 {"vts_exec_size", required_argument, 0, 'e'}, 64 {"vts_seed", required_argument, 0, 's'}, 65 {"vts_target_iface", required_argument, 0, 't'}}; 66 67 // Removes information from CompSpec not needed by fuzzer. 68 static void TrimCompSpec(CompSpec *comp_spec) { 69 if (comp_spec == nullptr) { 70 cerr << __func__ << ": empty CompSpec." << endl; 71 return; 72 } 73 if (comp_spec->has_interface()) { 74 auto *iface_spec = comp_spec->mutable_interface(); 75 for (auto i = 0; i < iface_spec->api_size(); ++i) { 76 iface_spec->mutable_api(i)->clear_callflow(); 77 } 78 } 79 } 80 81 static vector<CompSpec> ExtractCompSpecs(string arg) { 82 vector<CompSpec> result{}; 83 string dir_path; 84 std::istringstream iss(arg); 85 86 while (std::getline(iss, dir_path, ':')) { 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 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 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 params.comp_specs_ = ExtractCompSpecs(optarg); 135 break; 136 case 'e': 137 params.exec_size_ = std::stoul(optarg); 138 break; 139 case 's': 140 params.seed_ = std::stoull(optarg); 141 break; 142 case 't': 143 params.target_iface_ = optarg; 144 break; 145 default: 146 // Ignore. This option will be handled by libfuzzer. 147 break; 148 } 149 } 150 return params; 151 } 152 153 string ProtoFuzzerParams::DebugString() const { 154 std::stringstream ss; 155 ss << "Execution size: " << exec_size_ << endl; 156 ss << "Target interface: " << target_iface_ << endl; 157 ss << "Binder mode: " << binder_mode_ << endl; 158 ss << "Seed: " << seed_ << endl; 159 ss << "Loaded specs: " << endl; 160 for (const auto &spec : comp_specs_) { 161 ss << spec.component_name() << endl; 162 } 163 return ss.str(); 164 } 165 166 unordered_map<string, TypeSpec> ExtractPredefinedTypes( 167 const vector<CompSpec> &specs) { 168 unordered_map<string, TypeSpec> predefined_types; 169 for (const auto &comp_spec : specs) { 170 for (const auto &var_spec : comp_spec.attribute()) { 171 ExtractPredefinedTypesFromVar(var_spec, predefined_types); 172 } 173 for (const auto &var_spec : comp_spec.interface().attribute()) { 174 ExtractPredefinedTypesFromVar(var_spec, predefined_types); 175 } 176 } 177 return predefined_types; 178 } 179 180 bool FromArray(const uint8_t *data, size_t size, ExecSpec *exec_spec) { 181 // TODO(b/63136690): Use checksum to validate exec_spec more reliably. 182 return exec_spec->ParseFromArray(data, size) && exec_spec->has_valid() && 183 exec_spec->valid(); 184 } 185 186 size_t ToArray(uint8_t *data, size_t size, ExecSpec *exec_spec) { 187 exec_spec->set_valid(true); 188 size_t exec_size = exec_spec->ByteSize(); 189 exec_spec->SerializeToArray(data, exec_size); 190 return exec_size; 191 } 192 193 } // namespace fuzzer 194 } // namespace vts 195 } // namespace android 196