| #!/system/bin/sh |
| # Copyright (C) 2018 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. |
| # Abort on any error |
| |
| # Global configuration |
| FUZZER_BASES=( "/data/nativetest64/fuzzers" "/data/nativetest/fuzzers" ) |
| WORK_BASE="/data/local/tmp/fuzz" |
| LAST_FUZZ="$WORK_BASE/last_session" |
| |
| #------------------------------ HELPER FUNCTIONS ------------------------------ |
| function die() { |
| echo "$@" >&2 |
| exit 1 |
| } |
| |
| # Print the usage and exit. |
| # Optionally takes an extra string to print. |
| function usage() { |
| die "\ |
| Usage: $0 [-a artifacts_path] [-c corpus] [-e engine] [-l logname] [-n new_corpus] [-w workdir] fuzzer [-- fuzzer_option...] |
| |
| Run a fuzzing session. |
| |
| All of the information on the most recent session is symlinked at $LAST_FUZZ, |
| which points to the most recent session's working directory. |
| |
| The working directory is standardized to contain the following directories (or symlinks, |
| if non-default options are provided): |
| |
| artifacts/ Crashing test cases and other fuzzer output |
| corpus/ Input corpus |
| corpus_new/ Mutated test cases which exercise coverage features not represented |
| in the input corpus. |
| fuzz.log Contents of stdout from the fuzzing session. |
| |
| Arguments: |
| fuzzer Name of the fuzzer, e.g. cxa_demangle_fuzzer, or a full path to the fuzzer |
| fuzzer_option Fuzzer-specific arguments |
| |
| Options: |
| -a Path where fuzzer artifacts (logs, timeouts, crashes) should be stored |
| (Default: \$WORK/artifacts) |
| |
| -c Path where input corpus is stored. Must not be empty. |
| (Default: \$WORK/corpus) |
| |
| -e Fuzzer engine to use. Defaults to libFuzzer. |
| |
| -l Filename to use for the fuzzer log. |
| (Default: fuzz.log) |
| |
| -n Path to store new corpus elements |
| (Default: \$WORK/corpus_new) |
| |
| -w Work directory to use |
| (Default: $WORK_BASE/\$FUZZER) |
| |
| Examples: |
| |
| $ adb shell fuzz IOMX |
| $ adb shell fuzz -e honggfuzz IOMX |
| $ adb shell fuzz -w /sdcard IOMX |
| $ adb shell fuzz -c /sdcard/corpus IOMX |
| $ adb shell fuzz /data/my_fuzzer -- --foo --bar |
| $@" |
| } |
| |
| # Create a symlink from $1 ==> $2, if $2 is specified. |
| # Otherwise, ensure $1 exists and is a directory. |
| function symlink_or_create() { |
| DIR=$1 |
| TARGET=$2 |
| if [ -n "$TARGET" ]; then |
| if [ ! -d "$TARGET" ]; then |
| die "Directory does not exist: $TARGET" |
| fi |
| rm -rf "$DIR" |
| ln -sv "$TARGET" "$DIR" |
| elif [ ! -d "$DIR" ]; then |
| mkdir -p "$DIR" |
| fi |
| } |
| |
| # Ensure that there is "enough space" left on the device for |
| # the path provided. |
| function ensure_space() { |
| FREE_SPACE=$(df $1 | tail -1 | awk '{print $4}') |
| TEN_MEGABYTES=$((1024*10)) |
| |
| if [ $FREE_SPACE -lt $TEN_MEGABYTES ]; then |
| die "Not enough free space available at $(realpath $1):\n$(df -h $1)" |
| fi |
| } |
| |
| # Ensure that the provided directory is not empty. |
| function ensure_not_empty() { |
| if [ "$(find -H $1 -type f | wc -l)" -eq "0" ]; then |
| die "$1 is empty" |
| fi |
| } |
| |
| #--------------------------- CHECK SYSTEM VIABILITY --------------------------- |
| # Make sure ASAN and coverage work |
| if ! sanitizer-status asan cov &>/dev/null; then |
| # repeat the command to show the output |
| die "Sanitizer Checks Failed!\n$(sanitizer-status)" |
| fi |
| |
| #------------------------------ CHECK ARGUMENTS ------------------------------- |
| # Determine what fuzzer we want to run, and ensure that it's on the device |
| OPT_ARTIFACTS="" |
| OPT_CORPUS="" |
| OPT_CORPUS_NEW="" |
| OPT_ENGINE="libFuzzer" |
| OPT_LOG="fuzz.log" |
| OPT_WORKDIR="" |
| |
| while getopts "a:c:e:l:n:w:" o; do |
| case "${o}" in |
| a) OPT_ARTIFACTS="${OPTARG}" ;; |
| c) OPT_CORPUS="${OPTARG}" ;; |
| e) OPT_ENGINE="${OPTARG}" ;; |
| l) OPT_LOG="${OPTARG}" ;; |
| n) OPT_CORPUS_NEW="${OPTARG}" ;; |
| w) OPT_WORKDIR="${OPTARG}" ;; |
| *) usage ;; |
| esac |
| done |
| |
| shift $((OPTIND-1)) |
| |
| if [ $# -lt 1 ]; then |
| usage "\nMissing arguments: fuzzer" |
| fi |
| |
| if [ -e $1 ]; then |
| FUZZER_BIN="$1" |
| FUZZER="$(basename $FUZZER_BIN)" |
| else |
| FUZZER="${1%_fuzzer}" |
| |
| for FUZZER_BASE in "${FUZZER_BASES[@]}"; do |
| FUZZER_BIN="${FUZZER_BASE}/${OPT_ENGINE}/${FUZZER}_fuzzer/${FUZZER}_fuzzer" |
| [ -e "$FUZZER_BIN" ] && break |
| done |
| fi |
| |
| shift |
| |
| if [ ! -e "$FUZZER_BIN" ]; then |
| die "Invalid fuzzer path $FUZZER_BIN: File does not exist" |
| fi |
| |
| #------------------------- CREATE DIRECTORY STRUCTURE ------------------------- |
| # First set up the root work directory. |
| # |
| # This directory is the default location where the corpus/, corpus_new/, and |
| # artifacts/ directories will be created. |
| # |
| # We also create symlinks here if any of those options are provided, so that |
| # the same directory structure is always available, and any external utilities |
| # can easily find the last fuzzing session's data. |
| WORK_ROOT="$WORK_BASE/$FUZZER" |
| symlink_or_create "$WORK_ROOT" "$OPT_WORKDIR" |
| WORK_ROOT="${OPT_WORKDIR:-$WORK_ROOT}" |
| |
| # Change into the work root, in case any of the OPT_XXX paths are relative. |
| cd "$WORK_ROOT" |
| |
| # Create the rest of the directory structure |
| ARTIFACTS="$WORK_ROOT/artifacts" |
| CORPUS="$WORK_ROOT/corpus" |
| CORPUS_NEW="$WORK_ROOT/corpus_new" |
| |
| symlink_or_create "$ARTIFACTS" "$OPT_ARTIFACTS" |
| symlink_or_create "$CORPUS" "$OPT_CORPUS" |
| symlink_or_create "$CORPUS_NEW" "$OPT_CORPUS_NEW" |
| |
| # Update the environment variables so that the "real" paths show up in |
| # the command invocation. |
| ARTIFACTS="${OPT_ARTIFACTS:-$ARTIFACTS}" |
| CORPUS="${OPT_CORPUS:-$CORPUS}" |
| CORPUS_NEW="${OPT_CORPUS_NEW:-$CORPUS_NEW}" |
| |
| # Check the contents of the corpus aren't empty, this indicates a user error. |
| ensure_not_empty "$CORPUS" |
| |
| # Ensure that there's room to grow the corpus / dump artifacts. |
| ensure_space "$CORPUS_NEW" |
| ensure_space "$ARTIFACTS" |
| |
| #------------------------- UPDATE ENVIRONMENT OPTIONS ------------------------- |
| # Set up the ASAN_OPTIONS for optimal everything |
| # Note that we are only appending options, so that we do not override the |
| # default-at-boot ASAN_OPTIONS (e.g include=/system/asan.options). |
| |
| ASAN_OPTIONS+=:coverage=1 |
| ASAN_OPTIONS+=:atexit=1 |
| # ASAN_OPTIONS+=:verbosity=2 |
| ASAN_OPTIONS+=:print_cmdline=1 |
| ASAN_OPTIONS+=:print_stats=1 |
| ASAN_OPTIONS+=:print_legend=1 |
| ASAN_OPTIONS+=:print_scariness=1 |
| ASAN_OPTIONS+=:log_path=/dev/null |
| |
| export ASAN_OPTIONS="$ASAN_OPTIONS" |
| echo "ASAN_OPTIONS=$ASAN_OPTIONS" |
| |
| |
| #---------------------------- BUILD FUZZER COMMAND ---------------------------- |
| # Based on the fuzzer engine selected, build up the command line. |
| case "${OPT_ENGINE}" in |
| libFuzzer) |
| # NOTE: We use '-jobs=-1' to get libFuzzer to fuzz forever. |
| set -A FUZZ_CMD -- \ |
| "$FUZZER_BIN" \ |
| -artifact_prefix="$ARTIFACTS" \ |
| -print_coverage=1 \ |
| -detect_leaks=0 \ |
| -jobs=-1 \ |
| "$CORPUS_NEW" \ |
| "$CORPUS" \ |
| "$@" |
| ;; |
| honggfuzz) |
| set -A FUZZ_CMD -- \ |
| honggfuzz \ |
| --persistent \ |
| --sanitizers \ |
| --tmout_sigvtalrm \ |
| --workspace "$ARTIFACTS" \ |
| --input "$CORPUS_NEW" \ |
| --covdir_new "$CORPUS_NEW" \ |
| -- "$FUZZER_BIN" "$@" |
| ;; |
| *) |
| die "Unknown fuzzer engine $FUZZER_TYPE" |
| ;; |
| esac |
| |
| #--------------------------------- RUN FUZZER --------------------------------- |
| # Set up a symlink for the "last fuzz session" so that we can easily find it. |
| ln -svf "$WORK_ROOT" "$LAST_FUZZ" |
| |
| # Change into the artifacts directory, so that anything |
| # the fuzzer emits to $PWD will also be captured. |
| cd "$ARTIFACTS" |
| |
| echo "Running fuzzer: ${FUZZ_CMD[@]}" |
| echo "------------------------------" |
| ${FUZZ_CMD[@]} | tee "${OPT_LOG}" |