split functionality and script

This commit is contained in:
kdeme 2019-09-30 15:41:15 +02:00 committed by zah
parent 1148f773d1
commit 91862ce65b
2 changed files with 107 additions and 67 deletions

View File

@ -1,36 +1,7 @@
import strformat, strutils
import ./fuzz_helpers
# Dependencies:
# - afl fuzzing: afl and gcc or clang/llvm
# - libFuzzer fuzzing: libFuzzer and clang/llvm
# - in afl experimental modes clang/llvm is also required
# TODO:
# - switch clang / gcc option for afl
# - afl init and persistent modes
# - parallel fuzzing
# - custom generate test cases from this script?
# - rerun testcases option (or create tests from failed cases)?
# - better cmd line parsing & and more options
const aflGcc = "--cc=gcc " &
"--gcc.exe=afl-gcc " &
"--gcc.linkerexe=afl-gcc"
const aflClang = "--cc=clang " &
"--clang.exe=afl-clang " &
"--clang.linkerexe=afl-clang"
const aflClangFast = "--cc=clang " &
"--clang.exe=afl-clang-fast " &
"--clang.linkerexe=afl-clang-fast"
const libFuzzerClang = "--cc=clang " &
"--passC='-fsanitize=fuzzer,address' " &
"--passL='-fsanitize=fuzzer,address'"
# Can also test in debug mode obviously, but might be slower
# Can turn on more logging, in case of libFuzzer it will get very verbose though
const defaultFlags = "-d:release -d:chronicles_log_level=fatal " &
"--hints:off --warnings:off --verbosity:0"
const inputDir = "input" # dir for input samples
const resultsDir = "results" # results dir (for afl)
# TODO: get this some nice cmd line options when confutils works for nimscript
# or if we want to put this in a nim application instead of script
if paramCount() < 3:
echo "Usage: nim fuzz.nims FUZZER TARGET"
@ -45,45 +16,13 @@ if not fileExists(targetPath):
echo "Target file does not exist"
quit 1
let splitFile = targetPath.rsplit("/", 1)
let workDir = splitFile[0]
let targetNimFile = splitFile[1]
var target = targetNimFile
target.removeSuffix(".nim")
cd workDir
case fuzzer
of "afl":
let aflOptions = &"-d:afl -d:noSignalHandler {aflGcc}"
let compileCmd = &"nim c {defaultFlags} {aflOptions} {target}"
exec compileCmd
if not dirExists(inputDir):
# create a input dir with one 0 file for afl
mkDir(inputDir)
withDir inputDir: exec "echo '0' > test"
var fuzzCmd: string
# if there is an output dir already, continue fuzzing from previous run
if not dirExists(resultsDir):
fuzzCmd = &"afl-fuzz -i {inputDir} -o {resultsDir} -M fuzzer01 -- ./{target}"
else:
fuzzCmd = &"afl-fuzz -i - -o {resultsDir} -M fuzzer01 -- ./{target}"
exec fuzzCmd
runFuzzer(targetPath, afl)
of "libFuzzer":
let libFuzzerOptions = &"--noMain {libFuzzerClang}"
let compileCmd = &"nim c {defaultFlags} {libFuzzerOptions} {target}"
exec compileCmd
if not dirExists(inputDir):
# libFuzzer is OK with empty input dir
mkDir(inputDir)
# TODO: could use {resultsDir} of afl here also to reuse as corpus
exec &"./{target} {inputDir}"
runFuzzer(targetPath, libFuzzer)
else:
echo "Invalid fuzzer option: ", fuzzer
echo "Fuzzer options are afl or libFuzzer"
quit 1
quit 1

View File

@ -0,0 +1,101 @@
import strformat, strutils
# Dependencies:
# - afl fuzzing: afl and gcc or clang/llvm
# - libFuzzer fuzzing: libFuzzer and clang/llvm
# - in afl experimental modes clang/llvm is also required
# TODO:
# - switch clang / gcc option for afl
# - afl init and persistent modes
# - parallel fuzzing options
# - custom generate test cases from this script?
# - rerun testcases option (or create tests from failed cases)
# - currently not cross platform
# - ...
const
aflGcc = "--cc=gcc " &
"--gcc.exe=afl-gcc " &
"--gcc.linkerexe=afl-gcc"
aflClang = "--cc=clang " &
"--clang.exe=afl-clang " &
"--clang.linkerexe=afl-clang"
aflClangFast = "--cc=clang " &
"--clang.exe=afl-clang-fast " &
"--clang.linkerexe=afl-clang-fast"
libFuzzerClang = "--cc=clang " &
"--passC='-fsanitize=fuzzer,address' " &
"--passL='-fsanitize=fuzzer,address'"
# Can also test in debug mode obviously, but might be slower
# Can turn on more logging, in case of libFuzzer it will get very verbose though
defaultFlags = "-d:release -d:chronicles_log_level=fatal "# &
# "--hints:off --warnings:off --verbosity:0"
type
Fuzzer* = enum
afl,
libFuzzer
Compiler* = enum
gcc = aflGcc,
clang = aflClang,
clangFast = aflClangFast
proc aflCompile*(target: string, c: Compiler) =
let aflOptions = &"-d:standalone -d:noSignalHandler {$c}"
let compileCmd = &"nim c {defaultFlags} {aflOptions} {target}"
exec compileCmd
proc aflExec*(target: string, inputDir: string, resultsDir: string,
cleanStart = false) =
if not dirExists(inputDir):
# create a input dir with one 0 file for afl
mkDir(inputDir)
withDir inputDir: exec "echo '0' > test"
var fuzzCmd: string
# if there is an output dir already, continue fuzzing from previous run
if (not dirExists(resultsDir)) or cleanStart:
fuzzCmd = &"afl-fuzz -i {inputDir} -o {resultsDir} -M fuzzer01 -- ./{target}"
else:
fuzzCmd = &"afl-fuzz -i - -o {resultsDir} -M fuzzer01 -- ./{target}"
exec fuzzCmd
proc libFuzzerCompile*(target: string) =
let libFuzzerOptions = &"--noMain {libFuzzerClang}"
let compileCmd = &"nim c {defaultFlags} {libFuzzerOptions} {target}"
exec compileCmd
proc libFuzzerExec*(target: string, corpusDir: string) =
if not dirExists(corpusDir):
# libFuzzer is OK when starting with empty corpus dir
mkDir(corpusDir)
exec &"./{target} {corpusDir}"
proc getDir*(path: string): string =
# TODO: This is not platform friendly at all.
let splitFile = path.rsplit("/", 1)
result = splitFile[0]
proc getTarget*(path: string): string =
# TODO: error handling
result = path
result.removeSuffix(".nim")
proc runFuzzer*(targetPath: string, fuzzer: Fuzzer) =
let
path = getDir(targetPath)
target = getTarget(targetPath)
case fuzzer
of afl:
aflCompile(targetPath, gcc)
aflExec(target, path & "/input", path & "/results")
of libFuzzer:
libFuzzerCompile(targetPath)
# Note: Lets not mix afl input with libFuzzer corpus default. This can have
# consequences on speed for afl. Better to look into merging afl results &
# libFuzzer corpus.
libFuzzerExec(target, path & "/corpus")