blob: 0732af8b38b7c394556b555daf8a5ee90dd43018 [file] [log] [blame]
/*
* 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