Skip to content

Commit

Permalink
Setup for persistent fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
quinox committed Feb 2, 2023
1 parent f78f2c8 commit d8cd4db
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 0 deletions.
22 changes: 22 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ endif()

add_compile_options(-Wall)

# Begin fuzz-persistent
# NB: These fuzzing targets require a special compiler!
add_custom_target(fuzzing_targets)
set_target_properties(fuzzing_targets PROPERTIES EXCLUDE_FROM_ALL TRUE)

file(GLOB all_implementations "${PROJECT_SOURCE_DIR}/*.cpp")
list(REMOVE_ITEM all_implementations "${PROJECT_SOURCE_DIR}/main.cpp") # we only want the main() function from the fuzz target
list(REMOVE_ITEM all_implementations "${PROJECT_SOURCE_DIR}/flashmqtestclient.cpp") # it causes problems: /usr/lib/gcc/x86_64-pc-linux-gnu/11.3.0/include/g++-v11/ext/new_allocator.h:162:23: error: call to deleted constructor of 'MqttPacket'
file(GLOB fuzz_targets "fuzz-persistent/targets/*.cpp")
foreach(file ${fuzz_targets})
cmake_path(GET file STEM fuzzstem)
set(fuzzbinary "fuzz_${fuzzstem}")
add_dependencies(fuzzing_targets ${fuzzbinary})
add_executable(${fuzzbinary} ${file})
target_sources(${fuzzbinary} PRIVATE ${all_implementations})
target_link_libraries(${fuzzbinary} dl ssl crypto resolv)
set_target_properties(${fuzzbinary} PROPERTIES EXCLUDE_FROM_ALL TRUE)

message(STATUS "Generated fuzzing target ${fuzzbinary}")
endforeach()
# End fuzz-persistent

add_executable(flashmq
forward_declarations.h
mainapp.h
Expand Down
2 changes: 2 additions & 0 deletions fuzz-persistent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build/
/output/
17 changes: 17 additions & 0 deletions fuzz-persistent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Fuzzing with persistent mode

See the [AFL++ documentation](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md) for generic help about fuzzing with persistent mode.

TL;DR: it's _faaaaaast_.

## Fuzzing everything

Simply run `./fuzz-helper.sh`. Crashes will be written to the folder called `output`.

## Setting up new test

To add a new test:

* Decide on a testname, the standard format is: `<sourcefilename>__<test name (fe. the function you're fuzzing)>`, for example `cirbuf__write`
* Create `target/${testname}.cpp`
* Run `./fuzz-helper.sh build_and_run ${testname}`
120 changes: 120 additions & 0 deletions fuzz-persistent/fuzz-helper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/bin/bash
#
# Script to build+run fuzzing targets in persistent mode using American Fuzzy Lop.

thisfile=$(readlink --canonicalize "$0")
thisdir=$(dirname "$thisfile")
projectdir=$(dirname "$thisdir")

die() {
>&2 echo "Fatal error: $*"
exit 9
}

if [[ -z "$AFL_ROOT" ]]; then
afl_fallback_from_path=$(command -v afl-fuzz)
if [[ -z "$afl_fallback_from_path" ]]
then
echo "ERROR: alf-fuzz not found. Please set the AFL_ROOT environment variable"
exit 1
else
AFL_ROOT=$(dirname "$afl_fallback_from_path")
echo "WARNING: You didn't set AFL_ROOT but I found afl-fuzz in $AFL_ROOT, I hope the other tools are also there"
fi
fi

set -u

usage() {
>&2 echo "Usage: [ build | run | build_and_run ]"
}
die() {
>&2 echo "A fatal error occurred: $*"
exit 9
}

COMMAND="build_and_run"
if [[ $# -ge 1 ]]; then
COMMAND="$1"
fi
SPECIFIC_BINARY=""
if [[ $# -ge 2 ]]; then
SPECIFIC_BINARY="$2"
fi

do_build() {
# We currently need LTO/fast because they understand the macros we use
export CC="$AFL_ROOT/afl-clang-fast"
CC_CLANG_LTO="$AFL_ROOT/afl-clang-lto"
[[ -e "$CC_CLANG_LTO" ]] && export CC="$CC_CLANG_LTO"

export CXX="$AFL_ROOT/afl-clang-fast++"
CXX_CLANG_LTO="$AFL_ROOT/afl-clang-lto++"
[[ -e "$CXX_CLANG_LTO" ]] && export CXX="$CXX_CLANG_LTO"

echo "Using for \$CC: $CC"
echo "Using for \$CXX: $CXX"

mkdir -p "${thisdir}/build"
cd "${thisdir}/build" || die "Something peculiar went wrong."

cmake -DCMAKE_BUILD_TYPE="fuzz-persistent" "$projectdir" || die "CMake failed."
if [[ -z "$SPECIFIC_BINARY" ]]
then
make -j "fuzzing_targets" || die "make failed."
else
make -j "fuzz_${SPECIFIC_BINARY}" || die "make failed."
fi
}

do_run() {

declare -a FUZZING_TARGETS
for i in "${thisdir}/build/fuzz_"*;
do
if [[ -x "$i" ]];
then
FUZZING_TARGETS+=("$(basename "$i")")
fi
done

echo "Fuzzing targets: ${FUZZING_TARGETS[*]}"

TMUXSESSION=flashmqfuzzpersistent
INPUT="${projectdir}/fuzztests"

if [[ -z "$SPECIFIC_BINARY" ]]
then
tmux new-session -s "$TMUXSESSION" -d "echo 'Cycle through the windows to see the fuzzing going on'; read"

for BINARY in "${FUZZING_TARGETS[@]}"
do
OUTPUT="${thisdir}/output/${BINARY}"
mkdir -p "$OUTPUT"
tmux new-window -t "$TMUXSESSION" -n "$BINARY" "'$AFL_ROOT/afl-fuzz' -i '${INPUT}' -o '${OUTPUT}' -T '$BINARY' -- '${thisdir}/build/$BINARY'; echo 'Press [enter] to exit'; read"
done
tmux next-window -t "$TMUXSESSION" # cycle through to the first window with the README
tmux attach-session -t "$TMUXSESSION"
else
OUTPUT="${thisdir}/output/${SPECIFIC_BINARY}"
BINARY="fuzz_$SPECIFIC_BINARY"
tmux new-session -s "$TMUXSESSION" -n "$BINARY" "'$AFL_ROOT/afl-fuzz' -i '${INPUT}' -o '${OUTPUT}' -T '$BINARY' -- '${thisdir}/build/$BINARY'; echo 'Press [enter] to exit'; read"
fi
}


if [[ "$COMMAND" == "build" ]]; then
>&2 echo "Building..."
do_build
elif [[ "$COMMAND" == "run" ]]; then
>&2 echo "Running..."
do_run
elif [[ "$COMMAND" == "build_and_run" ]]; then
>&2 echo "Building and running..."
do_build
do_run
else
>&2 echo "Unknown option $COMMAND"
usage
exit 1
fi
27 changes: 27 additions & 0 deletions fuzz-persistent/targets/cirbuf__write.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <unistd.h>
#include "../../cirbuf.h"

__AFL_FUZZ_INIT();

int main()
{
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif

unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT
// and before __AFL_LOOP!

while (__AFL_LOOP(10000))
{

int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a
// call!

CirBuf cirbuf(1024);

cirbuf.write(buf, len);
}

return 0;
}
25 changes: 25 additions & 0 deletions fuzz-persistent/targets/utils__base64Encode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <unistd.h>
#include "../../utils.h"

__AFL_FUZZ_INIT();

int main()
{
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif

unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT
// and before __AFL_LOOP!

while (__AFL_LOOP(10000))
{

int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a
// call!

base64Encode(buf, len);
}

return 0;
}
4 changes: 4 additions & 0 deletions utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ std::vector<char> base64Decode(const std::string &s)

std::string base64Encode(const unsigned char *input, const int length)
{
if (length == 13)
{
throw std::runtime_error("Test kaboom.");
}
const int pl = 4*((length+2)/3);
char *output = reinterpret_cast<char *>(calloc(pl+1, 1));
const int ol = EVP_EncodeBlock(reinterpret_cast<unsigned char *>(output), input, length);
Expand Down

0 comments on commit d8cd4db

Please sign in to comment.