/* * Copyright 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ProtoFuzzerUtils.h" #include #include #include #include #include "utils/InterfaceSpecUtil.h" using android::base::Split; using std::cerr; using std::cout; using std::string; using std::unordered_map; using std::vector; namespace android { namespace vts { namespace fuzzer { static void usage() { cout << "Usage:\n" "\n" "./vts_proto_fuzzer -- \n" "\n" "VTS flags (strictly in form --flag=value):\n" "\n" "\tvts_binder_mode: if set, fuzzer will open the HAL in binder mode.\n" "\tvts_exec_size: number of function calls per 1 run of " "LLVMFuzzerTestOneInput.\n" "\tvts_spec_dir: \":\"-separated list of directories on the target " "containing .vts spec files.\n" "\tvts_target_fq_name: fully-qualified name of interface targeted for " "fuzz version, e.g. " "\"android.hardware.nfc@1.1::INfc\".\n" "\tvts_seed: optional integral argument used to initalize the random " "number generator.\n" "\n" "libfuzzer flags (strictly in form -flag=value):\n" "\tUse -help=1 to see libfuzzer flags\n" "\n"; } static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"vts_binder_mode", no_argument, 0, 'b'}, {"vts_spec_dir", required_argument, 0, 'd'}, {"vts_exec_size", required_argument, 0, 'e'}, {"vts_seed", required_argument, 0, 's'}, {"vts_target_fq_name", required_argument, 0, 't'}}; // Removes information from CompSpec not needed by fuzzer. static void TrimCompSpec(CompSpec *comp_spec) { if (comp_spec == nullptr) { cerr << __func__ << ": empty CompSpec." << endl; return; } if (comp_spec->has_interface()) { auto *iface_spec = comp_spec->mutable_interface(); for (auto i = 0; i < iface_spec->api_size(); ++i) { iface_spec->mutable_api(i)->clear_callflow(); } } } vector ExtractCompSpecs(const vector &dirs) { vector result{}; for (const auto &dir_path : dirs) { DIR *dir; struct dirent *ent; if (!(dir = opendir(dir_path.c_str()))) { cerr << "Could not open directory: " << dir_path << endl; std::abort(); } while ((ent = readdir(dir))) { string vts_spec_name{ent->d_name}; if (vts_spec_name.find(".vts") != string::npos) { string vts_spec_path = dir_path + "/" + vts_spec_name; CompSpec comp_spec{}; ParseInterfaceSpec(vts_spec_path.c_str(), &comp_spec); TrimCompSpec(&comp_spec); result.emplace_back(std::move(comp_spec)); } } } return result; } static void ExtractPredefinedTypesFromVar( const TypeSpec &var_spec, unordered_map &predefined_types) { predefined_types[var_spec.name()] = var_spec; // Find all nested struct declarations. for (const auto &sub_var_spec : var_spec.sub_struct()) { ExtractPredefinedTypesFromVar(sub_var_spec, predefined_types); } // Find all nested union declarations. for (const auto &sub_var_spec : var_spec.sub_union()) { ExtractPredefinedTypesFromVar(sub_var_spec, predefined_types); } } ProtoFuzzerParams ExtractProtoFuzzerParams(int argc, char **argv) { ProtoFuzzerParams params; int opt = 0; int index = 0; while ((opt = getopt_long_only(argc, argv, "", long_options, &index)) != -1) { switch (opt) { case 'h': usage(); std::abort(); case 'b': params.binder_mode_ = true; break; case 'd': // optarg is a column-separated list of directories params.comp_specs_ = ExtractCompSpecs(Split(optarg, ":")); break; case 'e': params.exec_size_ = std::stoul(optarg); break; case 's': params.seed_ = std::stoull(optarg); break; case 't': { if (!FQName::parse(optarg, ¶ms.target_fq_name_)) { usage(); std::abort(); } break; } default: // Ignore. This option will be handled by libfuzzer. break; } } return params; } string ProtoFuzzerParams::DebugString() const { std::stringstream ss; ss << "Execution size: " << exec_size_ << endl; ss << "Target FQ name: " << target_fq_name_.string() << endl; ss << "Binder mode: " << binder_mode_ << endl; ss << "Seed: " << seed_ << endl; ss << "Loaded specs: " << endl; for (const auto &spec : comp_specs_) { ss << spec.component_name() << endl; } return ss.str(); } unordered_map ExtractPredefinedTypes( const vector &specs) { unordered_map predefined_types; for (const auto &comp_spec : specs) { for (const auto &var_spec : comp_spec.attribute()) { ExtractPredefinedTypesFromVar(var_spec, predefined_types); } for (const auto &var_spec : comp_spec.interface().attribute()) { ExtractPredefinedTypesFromVar(var_spec, predefined_types); } } return predefined_types; } bool FromArray(const uint8_t *data, size_t size, ExecSpec *exec_spec) { // TODO(b/63136690): Use checksum to validate exec_spec more reliably. return exec_spec->ParseFromArray(data, size) && exec_spec->has_valid() && exec_spec->valid(); } size_t ToArray(uint8_t *data, size_t size, ExecSpec *exec_spec) { exec_spec->set_valid(true); size_t exec_size = exec_spec->ByteSize(); exec_spec->SerializeToArray(data, exec_size); return exec_size; } } // namespace fuzzer } // namespace vts } // namespace android