blob: bf9980a948847d9ea5235e6b24c02fc8f31c9e77 [file] [log] [blame]
# 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)