/* * 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 <dirent.h> #include <dlfcn.h> #include <getopt.h> #include <algorithm> #include <sstream> #include "specification_parser/InterfaceSpecificationParser.h" #include "utils/InterfaceSpecUtil.h" using std::cout; using std::cerr; using std::string; using std::unordered_map; using std::vector; namespace android { namespace vts { namespace fuzzer { static void usage() { fprintf( stdout, "Usage:\n" "\n" "./<fuzzer> <vts flags> -- <libfuzzer flags>\n" "\n" "VTS flags (strictly in form --flag=value):\n" "\n" " vts_spec_files \tColumn-separated list of paths to vts spec files.\n" " vts_exec_size \t\tNumber of function calls per fuzzer execution.\n" "\n" "libfuzzer flags (strictly in form -flag=value):\n" " Use -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_service_name", required_argument, 0, 's'}, {"vts_target_iface", required_argument, 0, 't'}}; static string GetDriverName(const CompSpec &comp_spec) { stringstream version; version.precision(1); version << fixed << comp_spec.component_type_version(); string driver_name = comp_spec.package() + "@" + version.str() + "-vts.driver.so"; return driver_name; } static string GetServiceName(const CompSpec &comp_spec) { // Infer HAL service name from its package name. string prefix = "android.hardware."; string service_name = comp_spec.package().substr(prefix.size()); return service_name; } // 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(); } } } static vector<CompSpec> ExtractCompSpecs(string dir_path) { vector<CompSpec> result{}; DIR *dir; struct dirent *ent; if (!(dir = opendir(dir_path.c_str()))) { cerr << "Could not open directory: " << dir_path << endl; exit(1); } 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{}; InterfaceSpecificationParser::parse(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<string, TypeSpec> &predefined_types) { predefined_types[var_spec.name()] = var_spec; for (const auto &sub_var_spec : var_spec.sub_struct()) { 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(); exit(0); case 'b': params.get_stub_ = false; break; case 'd': params.comp_specs_ = ExtractCompSpecs(optarg); break; case 'e': params.exec_size_ = atoi(optarg); break; case 's': params.service_name_ = optarg; break; case 't': params.target_iface_ = optarg; break; default: // Ignore. This option will be handled by libfuzzer. break; } } return params; } CompSpec FindTargetCompSpec(const vector<CompSpec> &specs, const string &target_iface) { if (target_iface.empty()) { cerr << "Target interface not specified." << endl; exit(1); } auto spec = std::find_if(specs.begin(), specs.end(), [target_iface](auto x) { return x.component_name().compare(target_iface) == 0; }); if (spec == specs.end()) { cerr << "Target interface doesn't match any of the loaded .vts files." << endl; exit(1); } return *spec; } // TODO(trong): this should be done using FuzzerWrapper. FuzzerBase *InitHalDriver(const CompSpec &comp_spec, string service_name, bool get_stub) { const char *error; string driver_name = GetDriverName(comp_spec); void *handle = dlopen(driver_name.c_str(), RTLD_LAZY); if (!handle) { cerr << __func__ << ": " << dlerror() << endl; cerr << __func__ << ": Can't load shared library: " << driver_name << endl; exit(-1); } // Clear dlerror(). dlerror(); string function_name = GetFunctionNamePrefix(comp_spec); using loader_func = FuzzerBase *(*)(); auto hal_loader = (loader_func)dlsym(handle, function_name.c_str()); if ((error = dlerror()) != NULL) { cerr << __func__ << ": Can't find: " << function_name << endl; cerr << error << endl; exit(1); } FuzzerBase *hal = hal_loader(); // For fuzzing, only passthrough mode provides coverage. if (get_stub) { cout << "HAL used in passthrough mode." << endl; } else { cout << "HAL used in binderized mode." << endl; } if (!hal->GetService(get_stub, service_name.c_str())) { cerr << __func__ << ": GetService(true, " << service_name << ") failed." << endl; exit(1); } return hal; } unordered_map<string, TypeSpec> ExtractPredefinedTypes( const vector<CompSpec> &specs) { unordered_map<string, TypeSpec> predefined_types; for (const auto &comp_spec : specs) { for (const auto &var_spec : comp_spec.attribute()) { ExtractPredefinedTypesFromVar(var_spec, predefined_types); } } return predefined_types; } void Execute(FuzzerBase *hal, const ExecSpec &exec_spec) { FuncSpec result{}; for (const auto &func_spec : exec_spec.api()) { hal->CallFunction(func_spec, "", &result); } } } // namespace fuzzer } // namespace vts } // namespace android