Home | History | Annotate | Download | only in iface_fuzzer
      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