| # Copyright 2020 Google LLC | |
| # | |
| # 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 | |
| # | |
| # https://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. | |
| # Lint as: python3 | |
| """ | |
| Wrapper tool providing a uniform flag interface for launching fuzz tests. | |
| This tool acts as a frontend to fuzz test launching. It receives fuzz test | |
| execution parameters as a set of user-provided flags and relays the | |
| configuration to a pluggable backend launcher script via environment | |
| variables. The backend launcher implements the engine-specific logic of | |
| translating the environment variables into the final command that launches | |
| the fuzzer executable. | |
| """ | |
| import os | |
| import shutil | |
| import sys | |
| from absl import app | |
| from absl import flags | |
| FLAGS = flags.FLAGS | |
| flags.DEFINE_string( | |
| "engine_launcher", None, | |
| "Path to a shell script that launches the fuzzing engine executable with the appropriate command line arguments." | |
| ) | |
| flags.DEFINE_string("binary_path", None, "Path to the fuzz test binary.") | |
| flags.DEFINE_bool( | |
| "regression", False, | |
| "If set True, the script will trigger the target as a regression test.") | |
| flags.DEFINE_integer( | |
| "timeout_secs", | |
| 0, | |
| "The maximum duration, in seconds, of the fuzzer run launched.", | |
| lower_bound=0) | |
| flags.DEFINE_string( | |
| "corpus_dir", None, | |
| "If non-empty, a directory that will be used as a seed corpus for the fuzzer." | |
| ) | |
| flags.DEFINE_string("dictionary_path", None, | |
| "If non-empty, a dictionary file of input keywords.") | |
| flags.DEFINE_string( | |
| "fuzzing_output_root", "/tmp/fuzzing", | |
| "The root directory for storing all generated artifacts during fuzzing.") | |
| flags.DEFINE_bool( | |
| "clean", False, | |
| "If set, cleans up the output directory of the target before fuzzing.") | |
| flags.mark_flag_as_required("engine_launcher") | |
| flags.mark_flag_as_required("binary_path") | |
| def main(argv): | |
| # TODO(sbucur): Obtain a target-specific path here. | |
| target_output_root = os.path.join(FLAGS.fuzzing_output_root) | |
| print("Using test output root: %s" % target_output_root, file=sys.stderr) | |
| if FLAGS.clean: | |
| print("Cleaning up the test output root before starting fuzzing...", | |
| file=sys.stderr) | |
| try: | |
| shutil.rmtree(target_output_root) | |
| except FileNotFoundError: | |
| pass | |
| os.makedirs(target_output_root, exist_ok=True) | |
| corpus_output_path = os.path.join(target_output_root, 'corpus') | |
| print('Writing new corpus elements at: %s' % corpus_output_path, | |
| file=sys.stderr) | |
| os.makedirs(corpus_output_path, exist_ok=True) | |
| artifacts_output_path = os.path.join(target_output_root, 'artifacts') | |
| print('Writing new artifacts at: %s' % artifacts_output_path) | |
| os.makedirs(artifacts_output_path, exist_ok=True) | |
| os.environ["FUZZER_BINARY"] = FLAGS.binary_path | |
| os.environ["FUZZER_TIMEOUT_SECS"] = str(FLAGS.timeout_secs) | |
| os.environ["FUZZER_IS_REGRESSION"] = "1" if FLAGS.regression else "0" | |
| os.environ["FUZZER_OUTPUT_ROOT"] = target_output_root | |
| os.environ["FUZZER_OUTPUT_CORPUS_DIR"] = corpus_output_path | |
| os.environ["FUZZER_ARTIFACTS_DIR"] = artifacts_output_path | |
| if FLAGS.dictionary_path: | |
| os.environ["FUZZER_DICTIONARY_PATH"] = FLAGS.dictionary_path | |
| if FLAGS.corpus_dir: | |
| os.environ["FUZZER_SEED_CORPUS_DIR"] = FLAGS.corpus_dir | |
| os.execv("/bin/bash", ["/bin/bash", FLAGS.engine_launcher] + argv[1:]) | |
| if __name__ == "__main__": | |
| app.run(main) |