diff --git a/.gitignore b/.gitignore index e752b94a2..ff6eeee4c 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ docker/generated.mk # Vim backup files. .*.swp + +# IntelliJ files +.idea diff --git a/fuzzers/sievefuzz/builder.Dockerfile b/fuzzers/sievefuzz/builder.Dockerfile new file mode 100644 index 000000000..757fce646 --- /dev/null +++ b/fuzzers/sievefuzz/builder.Dockerfile @@ -0,0 +1,61 @@ +# 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 +# +# 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. + +ARG parent_image +FROM $parent_image + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt update && \ + apt install -y \ + silversearcher-ag beanstalkd gdb screen patchelf apt-transport-https ca-certificates clang-9 libclang-9-dev\ + gcc-7 g++-7 sudo curl wget build-essential make cmake ninja-build git subversion python3 python3-dev python3-pip autoconf automake &&\ + python3 -m pip install --upgrade pip && python3 -m pip install greenstalk psutil + +RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-9 10 \ + --slave /usr/bin/clang++ clang++ /usr/bin/clang++-9 \ + --slave /usr/bin/opt opt /usr/bin/opt-9 +RUN update-alternatives --install /usr/lib/llvm llvm /usr/lib/llvm-9 20 \ + --slave /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-9 \ + --slave /usr/bin/llvm-link llvm-link /usr/bin/llvm-link-9 + + +RUN apt-get -y install locales && locale-gen en_US.UTF-8 +ENV LC_ALL="en_US.UTF-8" + +# Download and compile SieveFuzz +RUN git clone https://github.com/HexHive/SieveFuzz /afl && \ + cd /afl && \ + git checkout 1751673ed6c56b7dc69b71ef07ace49867e3cfa4 && \ + cd third_party && ./install_svf.sh + +RUN cd /afl/third_party && unset CFLAGS CXXFLAGS && \ + export CC=clang AFL_NO_X86=1 && \ + PYTHON_INCLUDE=/ && ./install_sievefuzz.sh + +RUN cp -r /afl/gllvm_bins /afl/third_party/SVF/Release-build/bin + + +# Use afl_driver.cpp from LLVM as our fuzzing library. +RUN apt-get update && \ + apt-get install wget -y && \ + wget https://raw.githubusercontent.com/llvm/llvm-project/5feb80e748924606531ba28c97fe65145c65372e/compiler-rt/lib/fuzzer/afl/afl_driver.cpp -O /afl/third_party/sievefuzz/afl_driver.cpp && \ + cd /afl/third_party/sievefuzz && \ + clang -D AF -D TRACE_METRIC -Wno-pointer-sign -c llvm_mode/afl-llvm-rt.o.c -I. -Iinclude && \ + clang++ -stdlib=libc++ -std=c++11 -O2 -c afl_driver.cpp && \ + ar r /libAFL.a afl_driver.o afl-llvm-rt.o + + + + diff --git a/fuzzers/sievefuzz/fuzzer.py b/fuzzers/sievefuzz/fuzzer.py new file mode 100755 index 000000000..3f44181e9 --- /dev/null +++ b/fuzzers/sievefuzz/fuzzer.py @@ -0,0 +1,155 @@ +# 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 +# +# 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. +"""Integration code for AFL fuzzer.""" + +import json +import os +import shutil +import subprocess + +from fuzzers import utils + + +def is_benchmark(name): + """Check if the benchmark contains the string |name|""" + benchmark = os.getenv('BENCHMARK', None) + return benchmark is not None and name in benchmark + +def fix_fuzzer_lib(): + """Fix FUZZER_LIB for certain benchmarks""" + + shutil.copy('/libAFL.a', '/usr/lib/libFuzzingEngine.a') + + +def prepare_build_environment(): + """Set environment variables used to build targets for AFL-based + fuzzers.""" + cflags = ['-fsanitize-coverage=trace-pc-guard'] + utils.append_flags('CFLAGS', cflags) + utils.append_flags('CXXFLAGS', cflags) + + os.environ['CC'] = 'clang' + os.environ['CXX'] = 'clang++' + os.environ['FUZZER_LIB'] = '/libAFL.a' + + # Fix FUZZER_LIB for various benchmarks. + fix_fuzzer_lib() + + +def build(): + """Build benchmark.""" + prepare_build_environment() + + utils.build_benchmark() + + print('[post_build] Copying afl-fuzz to $OUT directory') + # Copy out the afl-fuzz binary as a build artifact. + shutil.copy('/afl/third_party/sievefuzz/afl-fuzz', os.environ['OUT']) + + +def get_stats(output_corpus, fuzzer_log): # pylint: disable=unused-argument + """Gets fuzzer stats for AFL.""" + # Get a dictionary containing the stats AFL reports. + stats_file = os.path.join(output_corpus, 'fuzzer_stats') + if not os.path.exists(stats_file): + print('Can\'t find fuzzer_stats') + return '{}' + with open(stats_file, encoding='utf-8') as file_handle: + stats_file_lines = file_handle.read().splitlines() + stats_file_dict = {} + for stats_line in stats_file_lines: + key, value = stats_line.split(': ') + stats_file_dict[key.strip()] = value.strip() + + # Report to FuzzBench the stats it accepts. + stats = {'execs_per_sec': float(stats_file_dict['execs_per_sec'])} + return json.dumps(stats) + + +def prepare_fuzz_environment(input_corpus): + """Prepare to fuzz with AFL or another AFL-based fuzzer.""" + # Tell AFL to not use its terminal UI so we get usable logs. + os.environ['AFL_NO_UI'] = '1' + # Skip AFL's CPU frequency check (fails on Docker). + os.environ['AFL_SKIP_CPUFREQ'] = '1' + # No need to bind affinity to one core, Docker enforces 1 core usage. + os.environ['AFL_NO_AFFINITY'] = '1' + # AFL will abort on startup if the core pattern sends notifications to + # external programs. We don't care about this. + os.environ['AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES'] = '1' + # Don't exit when crashes are found. This can happen when corpus from + # OSS-Fuzz is used. + os.environ['AFL_SKIP_CRASHES'] = '1' + # Shuffle the queue + os.environ['AFL_SHUFFLE_QUEUE'] = '1' + + # AFL needs at least one non-empty seed to start. + utils.create_seed_file_for_empty_corpus(input_corpus) + + +def check_skip_det_compatible(additional_flags): + """ Checks if additional flags are compatible with '-d' option""" + # AFL refuses to take in '-d' with '-M' or '-S' options for parallel mode. + # (cf. https://github.com/google/AFL/blob/8da80951/afl-fuzz.c#L7477) + if '-M' in additional_flags or '-S' in additional_flags: + return False + return True + + +def run_afl_fuzz(input_corpus, + output_corpus, + target_binary, + additional_flags=None, + hide_output=False): + """Run afl-fuzz.""" + # Spawn the afl fuzzing process. + print('[run_afl_fuzz] Running target with sievefuzz') + command = [ + './afl-fuzz', + '-i', + input_corpus, + '-o', + output_corpus, + # Use no memory limit as ASAN doesn't play nicely with one. + '-m', + 'none', + '-t', + '1000+', # Use same default 1 sec timeout, but add '+' to skip hangs. + ] + # Use '-d' to skip deterministic mode, as long as it it compatible with + # additional flags. + if not additional_flags or check_skip_det_compatible(additional_flags): + command.append('-d') + if additional_flags: + command.extend(additional_flags) + dictionary_path = utils.get_dictionary_path(target_binary) + if dictionary_path: + command.extend(['-x', dictionary_path]) + command += [ + '--', + target_binary, + # Pass INT_MAX to afl the maximize the number of persistent loops it + # performs. + '2147483647' + ] + print('[run_afl_fuzz] Running command: ' + ' '.join(command)) + output_stream = subprocess.DEVNULL if hide_output else None + subprocess.check_call(command, stdout=output_stream, stderr=output_stream) + + +def fuzz(input_corpus, output_corpus, target_binary): + """Run afl-fuzz on target.""" + prepare_fuzz_environment(input_corpus) + + run_afl_fuzz(input_corpus, output_corpus, target_binary) diff --git a/fuzzers/sievefuzz/runner.Dockerfile b/fuzzers/sievefuzz/runner.Dockerfile new file mode 100644 index 000000000..43a708a0e --- /dev/null +++ b/fuzzers/sievefuzz/runner.Dockerfile @@ -0,0 +1,16 @@ +# 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 +# +# 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. + +FROM gcr.io/fuzzbench/base-image +RUN apt-get install -y python3.8-dev