diff --git a/fuzzing/fuzz/Android.bp b/fuzzing/fuzz/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..1057c17d4e1ab574047175a534d4d899254075cb --- /dev/null +++ b/fuzzing/fuzz/Android.bp @@ -0,0 +1,5 @@ +cc_prebuilt_binary { + name: "fuzz", + srcs: ["fuzz"] +} + diff --git a/fuzzing/fuzz/fuzz b/fuzzing/fuzz/fuzz new file mode 100644 index 0000000000000000000000000000000000000000..5d0975418db384c1d764b2c15e8cf90670f3b516 --- /dev/null +++ b/fuzzing/fuzz/fuzz @@ -0,0 +1,263 @@ +#!/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_BASE="/data/nativetest64/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}" + FUZZER_BIN="${FUZZER_BASE}/${OPT_ENGINE}/${FUZZER}_fuzzer" + + if [ ! -e "$FUZZER_BIN" ]; then + die "Invalid fuzzer name ${FUZZER}: ($FUZZER_BIN does not exist)" + fi +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}"