ronnie
2022-10-14 1504bb53e29d3d46222c0b3ea994fc494b48e153
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
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_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}"