201 lines
6.1 KiB
C++
201 lines
6.1 KiB
C++
/*
|
|
* 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 <android-base/strings.h>
|
|
#include <dirent.h>
|
|
#include <getopt.h>
|
|
#include <algorithm>
|
|
|
|
#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 <vts flags> -- <libfuzzer flags>\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<CompSpec> ExtractCompSpecs(const vector<string> &dirs) {
|
|
vector<CompSpec> 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<string, TypeSpec> &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<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);
|
|
}
|
|
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
|