Added libfuzzer support.

This commit is contained in:
bambam 2020-05-09 21:30:32 +00:00 committed by Jack Grigg
parent f8fa2fcad0
commit 58dda603a4
64 changed files with 571 additions and 15 deletions

View File

@ -2,11 +2,43 @@
#include "chainparams.h"
#include "proof_verifier.h"
int main (int argc, char *argv[]) {
extern bool CheckBlock(
const CBlock& block,
CValidationState& state,
const CChainParams& chainparams,
libzcash::ProofVerifier& verifier,
bool fCheckPOW = true,
bool fCheckMerkleRoot = true);
bool init_done = false;
const CChainParams& chainparams = NULL;
auto verifier = libzcash::ProofVerifier::Strict();
int fuzz_CheckBlock(CBlock block) {
int retval = 0;
SelectParams(CBaseChainParams::MAIN);
if (!init_done) {
SelectParams(CBaseChainParams::MAIN);
chainparams = Params()
init_done = true;
}
CValidationState state;
// We don't check the PoW or Merkle tree root in order to reach more code.
if (!CheckBlock(block, state, chainparams, verifier, false, false)) {
retval = -1;
}
return retval;
}
#ifdef FUZZ_WITH_AFL
int main (int argc, char *argv[]) {
CBlock block;
CAutoFile filein(fopen(argv[1], "rb"), SER_DISK, CLIENT_VERSION);
try {
@ -14,17 +46,22 @@ int main (int argc, char *argv[]) {
} catch (const std::exception& e) {
return -1;
}
// We don't load the SNARK parameters because it's too slow. This means that
// valid blocks with shielded transactions will generate a crash.
const CChainParams& chainparams = Params();
auto verifier = ProofVerifier::Disabled();
CValidationState state;
// We don't check the PoW or Merkle tree root in order to reach more code.
if (!CheckBlock(block, state, chainparams, verifier, false, false)) {
retval = -1;
}
return retval;
return fuzz_CheckBlock(block);
}
#endif // FUZZ_WITH_AFL
#ifdef FUZZ_WITH_LIBFUZZER
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
CBlock block;
CDataStream ds((const char *)Data, (const char *)Data+Size, SER_NETWORK, PROTOCOL_VERSION);
try {
ds >> block;
} catch (const std::exception &e) {
return -1;
}
fuzz_CheckBlock(block);
return 0; // Non-zero return values are reserved for future use.
}
#endif

View File

@ -1,10 +1,18 @@
#include <bits/stdc++.h>
#include <string>
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
// actual fuzzer
bool fuzz_DecodeHexTxFunction (const std::string& strHexTx) {
CTransaction tx;
return DecodeHexTx(tx, strHexTx);
}
#ifdef FUZZ_WITH_AFL
// AFL
int fuzz_DecodeHexTx (int argc, char *argv[]) {
std::ifstream t(argv[1]);
@ -15,3 +23,17 @@ int fuzz_DecodeHexTx (int argc, char *argv[]) {
}
int main (int argc, char *argv[]) { return fuzz_DecodeHexTx(argc, argv); }
#endif
#ifdef FUZZ_WITH_LIBFUZZER
// libfuzzer
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
std::string s;
s.assign((const char *)Data, Size);
fuzz_DecodeHexTxFunction (s);
return 0; // Non-zero return values are reserved for future use.
}
#endif

View File

@ -1,6 +1,9 @@
#include "addrman.h"
#include "streams.h"
#ifdef FUZZ_WITH_AFL
int main (int argc, char *argv[]) {
CAddrMan addrman;
CAutoFile filein(fopen(argv[1], "rb"), SER_DISK, CLIENT_VERSION);
@ -11,3 +14,20 @@ int main (int argc, char *argv[]) {
return -1;
}
}
#endif
#ifdef FUZZ_WITH_LIBFUZZER
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
CAddrMan addrman;
CDataStream ds(Data, Data+Size, SER_DISK, CLIENT_VERSION);
try {
ds >> addrman;
} catch (const std::exception &e) {
return 0;
}
return 0; // Non-zero return values are reserved for future use.
}
#endif

View File

@ -1,3 +1,7 @@
#include <bits/stdc++.h>
// actual fuzzer
bool fuzz_TxDeserializeFunction (const std::vector<unsigned char> txData) {
CTransaction tx;
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
@ -9,6 +13,9 @@ bool fuzz_TxDeserializeFunction (const std::vector<unsigned char> txData) {
}
}
#ifdef FUZZ_WITH_AFL
// AFL
int fuzz_TxDeserialize (int argc, char *argv[]) {
std::ifstream t(argv[1]);
@ -19,3 +26,18 @@ int fuzz_TxDeserialize (int argc, char *argv[]) {
}
int main (int argc, char *argv[]) { return fuzz_TxDeserialize(argc, argv); }
#endif
#ifdef FUZZ_WITH_LIBFUZZER
// libFuzzer
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
std::vector<unsigned char> vect(Size);
memcpy(vect.data(), Data, Size);
fuzz_TxDeserializeFunction(vect);
return 0; // Non-zero return values are reserved for future use.
}
#endif

View File

@ -1,5 +1,7 @@
#include "txmempool.h"
#ifdef FUZZ_WITH_AFL
int main (int argc, char *argv[]) {
CFeeRate rate;
CTxMemPool mempool(rate);
@ -11,3 +13,22 @@ int main (int argc, char *argv[]) {
return -1;
}
}
#endif
#ifdef FUZZ_WITH_LIBFUZZER
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
CFeeRate rate;
CTxMemPool mempool(rate);
CAutoFile est_filein(fmemopen(Data, Size, "rb"), SER_DISK, CLIENT_VERSION);
if (mempool.ReadFeeEstimates(est_filein)) {
return 0;
} else {
return -1;
}
}
#endif

View File

@ -0,0 +1,7 @@
kw1="{"
kw2="}"
kw5="["
kw6="]"
kw7=":"
kw8=","
kw9="\""

View File

@ -0,0 +1,29 @@
#include "univalue.h"
int fuzz_UniValue_Read(std::string notquitejson) {
UniValue valRequest;
if (!valRequest.read(notquitejson)) {
return -1;
}
return 0;
}
#ifdef FUZZ_WITH_AFL
#error "The AFL version of this fuzzer has not yet been implemented."
int main (int argc, char *argv[]) {
// not implemented
return 0;
}
#endif // FUZZ_WITH_AFL
#ifdef FUZZ_WITH_LIBFUZZER
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
std::string s;
s.assign((const char *)Data, Size);
return fuzz_UniValue_Read(s);
}
#endif

View File

@ -0,0 +1 @@
"This is a string that never ends, yes it goes on and on, my friends.

View File

@ -0,0 +1 @@
{"Extra value after close": true} "misplaced quoted value"

View File

@ -0,0 +1 @@
{"Illegal expression": 1 + 2}

View File

@ -0,0 +1 @@
{"Illegal invocation": alert()}

View File

@ -0,0 +1 @@
{"Numbers cannot have leading zeroes": 013}

View File

@ -0,0 +1 @@
{"Numbers cannot be hex": 0x14}

View File

@ -0,0 +1 @@
["Illegal backslash escape: \x15"]

View File

@ -0,0 +1 @@
[\naked]

View File

@ -0,0 +1 @@
["Illegal backslash escape: \017"]

View File

@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]

View File

@ -0,0 +1 @@
{"Missing colon" null}

View File

@ -0,0 +1 @@
["Unclosed array"

View File

@ -0,0 +1 @@
{"Double colon":: null}

View File

@ -0,0 +1 @@
{"Comma instead of colon", null}

View File

@ -0,0 +1 @@
["Colon instead of comma": false]

View File

@ -0,0 +1 @@
["Bad value", truth]

View File

@ -0,0 +1 @@
['single quote']

View File

@ -0,0 +1 @@
[" tab character in string "]

View File

@ -0,0 +1 @@
["tab\ character\ in\ string\ "]

View File

@ -0,0 +1,2 @@
["line
break"]

View File

@ -0,0 +1,2 @@
["line\
break"]

View File

@ -0,0 +1 @@
[0e]

View File

@ -0,0 +1 @@
{unquoted_key: "keys must be quoted"}

View File

@ -0,0 +1 @@
[0e+]

View File

@ -0,0 +1 @@
[0e+-1]

View File

@ -0,0 +1 @@
{"Comma instead if closing brace": true,

View File

@ -0,0 +1 @@
["mismatch"}

View File

@ -0,0 +1 @@
{} garbage

View File

@ -0,0 +1 @@
[ true true true [] [] [] ]

View File

@ -0,0 +1 @@
{"a":}

View File

@ -0,0 +1 @@
{"a":1 "b":2}

View File

@ -0,0 +1 @@
["\ud834"]

View File

@ -0,0 +1 @@
["\udd61"]

View File

@ -0,0 +1 @@
["extra comma",]

View File

@ -0,0 +1 @@
["揣。"]

View File

@ -0,0 +1 @@
["<22><><EFBFBD>"]

Binary file not shown.

View File

@ -0,0 +1 @@
["double extra comma",,]

View File

@ -0,0 +1 @@
[ , "<-- missing value"]

View File

@ -0,0 +1 @@
["Comma after the close"],

View File

@ -0,0 +1 @@
["Extra close"]]

View File

@ -0,0 +1 @@
{"Extra comma": true,}

View File

@ -0,0 +1,58 @@
[
"JSON Test Pattern pass1",
{"object with 1 member":["array with 1 element"]},
{},
[],
-42,
true,
false,
null,
{
"integer": 1234567890,
"real": -9876.543210,
"e": 0.123456789e-12,
"E": 1.234567890E+34,
"": 23456789012E66,
"zero": 0,
"one": 1,
"space": " ",
"quote": "\"",
"backslash": "\\",
"controls": "\b\f\n\r\t",
"slash": "/ & \/",
"alpha": "abcdefghijklmnopqrstuvwyz",
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
"digit": "0123456789",
"0123456789": "digit",
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
"true": true,
"false": false,
"null": null,
"array":[ ],
"object":{ },
"address": "50 St. James Street",
"url": "http://www.JSON.org/",
"comment": "// /* <!-- --",
"# -- --> */": " ",
" s p a c e d " :[1,2 , 3
,
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
"quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
},
0.5 ,98.6
,
99.44
,
1066,
1e1,
0.1e1,
1e-1,
1e00,2e+00,2e-00
,"rosebud"]

View File

@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]

View File

@ -0,0 +1,6 @@
{
"JSON Test Pattern pass3": {
"The outermost value": "must be an object or array.",
"In this test": "It is an object."
}
}

View File

@ -0,0 +1 @@
["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f"]

View File

@ -0,0 +1 @@
["a§■𐎒𝅘𝅥𝅯"]

View File

@ -0,0 +1 @@
"abcdefghijklmnopqrstuvwxyz"

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
false

View File

@ -0,0 +1 @@
null

View File

@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -eu -o pipefail
set +x
function cmd_pref() {
if type -p "$2" > /dev/null; then
@ -69,6 +70,12 @@ as --version
ld -v
HOST="$HOST" BUILD="$BUILD" "$MAKE" "$@" -C ./depends/
if [ "${BUILD_STAGE:-all}" = "depends" ]
then
exit 0
fi
./autogen.sh
CONFIG_SITE="$PWD/depends/$HOST/share/config.site" ./configure $CONFIGURE_FLAGS
"$MAKE" "$@"

View File

@ -0,0 +1,151 @@
#!/usr/bin/env bash
set -eu -o pipefail
usage() {
echo ""
echo "$0 <build stage> <options> <passthrough build arguments>"
echo ""
echo "Build a fuzzer in the local repo using the following options:"
echo ""
echo "<build stage>:"
echo " either \"depends\" or \"fuzzer\""
echo ""
echo "<options>:"
echo " -f,--fuzzer <fuzzername> (ignored if building \"depends\")"
echo " [-s,--sanitizers <sanitizers>]"
echo " [-i,--instrument <instrument>]"
echo " [-l,--logfile <logfile>] # default is ./zcash-build-wrapper.log"
echo " [-h,--help]"
echo ""
echo "Where fuzzer is an entry in ./src/fuzzing/*, the default sanitizer"
echo "is \"address\" and default instrument is ( \"^.*/src/$\" )."
echo ""
exit -1
}
die() {
echo $1
exit -1
}
# parse command line options
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-f|--fuzzer)
FUZZER_NAME="$2"
if [ ! -f "./src/fuzzing/$FUZZER_NAME/fuzz.cpp" ]
then
die "Cannot find source code for fuzzer."
fi
shift
shift
;;
-s|--sanitizers)
LLVM_SANITIZERS="$2"
shift
shift
;;
-i|--instrument)
INSTRUMENT_CODE="$2"
shift
shift
;;
-l|--logfile)
LOGFILE="$2"
shift
shift
;;
-h|--help)
usage
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
# positional arguments
if [ ${#POSITIONAL[@]} -lt 1 ]
then
usage
else
BUILD_STAGE=${POSITIONAL[0]}
fi
case "${BUILD_STAGE:-undefined}" in
depends)
FUZZER_NAME=notused
# fine
;;
fuzzer)
# fine
;;
*)
# not fine
usage
;;
esac
# required arguments
if [ "${FUZZER_NAME:-undefined}" = "undefined" ]
then
usage
fi
# default values
if [ "${LLVM_SANITIZERS:-undefined}" = "undefined" ]
then
export LLVM_SANITIZERS="address"
fi
if [ "${INSTRUMENT_CODE:-undefined}" = "undefined" ]
then
export INSTRUMENT_CODE=("^.*\/src")
fi
if [ "${LOGFILE:-undefined}" = "undefined" ]
then
export LOGFILE=./zcash-build-wrapper.log
fi
set -x
export ZCUTIL=$(realpath "./zcutil")
# overwrite the Linux make profile to use clang instead of GCC:
cat $ZCUTIL/../depends/hosts/linux.mk | sed -e 's/=gcc/=clang/g' | sed -e 's/=g++/=clang++/g' > x
mv x $ZCUTIL/../depends/hosts/linux.mk
# the build_stage distinction helps to layer an intermediate docker
# container for the built dependencies, so we can resume building
# from there assuming no other build arguments have changed
if [ "$BUILD_STAGE" = "depends" ]
then
# make an empty fuzz file just so we can build dependencies
> src/fuzz.cpp
else
cp "./src/fuzzing/$FUZZER_NAME/fuzz.cpp" src/fuzz.cpp || die "Can't copy fuzz.cpp for that fuzzer"
fi
# sneak the variable into zcashd's build.sh
export BUILD_STAGE
# run build.sh with our compiler wrapper, and BUILD_STAGE environment set:
CONFIGURE_FLAGS="--enable-tests=no --disable-bench" \
"$ZCUTIL/build.sh" \
-j$(nproc) \
"CC=$ZCUTIL/libfuzzer/zcash-wrapper-clang" \
"CXX=$ZCUTIL/libfuzzer/zcash-wrapper-clang++" "${POSITIONAL[@]:1}" || die "Build failed at stage $BUILD_STAGE."

123
zcutil/libfuzzer/zcash-wrapper Executable file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env bash
# you shouldn't normally be calling this script directly, it should be called
# by build.sh, but only when build.sh is invoked from libfuzzer-build.sh.
set -ex -o pipefail
# logging functions
export green="\e[92m"
export red="\e[95m"
export normal="\e[0m"
export color=$normal
function log {
while read line
do
(echo ; echo -e "${color}${line}${normal}") >> "$LOGFILE" ;
done
}
# command-line parsing
export FINAL_LINK=0 # by default we using -fsanitize=fuzzer-no-link
for arg do
shift
if [ "$arg" = "-fPIE" ] # no more pie
then
set -- "$@" "-fPIC" "-fno-pie"
continue
fi
if [ "$arg" = "-pie" ]
then
set -- "$@" "-no-pie"
continue
fi
if [ "$arg" = "-o" ] # here an output file is being specified
then
if [ "$1" = "zcashd" ] # here zcashd is the output file
then
export FINAL_LINK=1 # we should use -fsanitize=fuzzer because that links libfuzzer
fi
# note no continue, we fall through to default set
fi
set -- "$@" "$arg"
done
# which source dirs to instrument
if [ "$dirs_to_instrument" = "" ]
then
export dirs_to_instrument=("^.*\/src$")
fi
# Store the command line we were given to a file
echo "`hostname`:`pwd` \$" | log
echo -- "original command: $0 $@" | log
# decide on a sanitizer option beyond just fuzzing:
# to link nor not to link libfuzzer we only link on
# the final call, to the linker.
if [ "$LLVM_SANITIZE" = "" ]
then
export LLVM_SANITIZE="address" # you can override this behavior by setting this environment variable first
fi
if [ "$FINAL_LINK" = "1" ]
then
export LLVM_SANITIZE="-fsanitize=fuzzer,$LLVM_SANITIZE"
else
export LLVM_SANITIZE="-fsanitize=fuzzer-no-link,$LLVM_SANITIZE"
fi
# Work out which compiler we were called as
case $0 in
*zcash-wrapper-clang)
COMPILER="clang"
export DEFINES_FLAGS="${CFLAGS} -DZCASH_FUZZ=1 -DFUZZ_WITH_LIBFUZZER=1 -fPIC"
export INSTRUMENT_FLAGS="${DEFINES_FLAGS} ${LLVM_SANITIZE}"
;;
*zcash-wrapper-clang++)
COMPILER="clang++"
export DEFINES_FLAGS="${CXXFLAGS} -DZCASH_FUZZ=1 -DFUZZ_WITH_LIBFUZZER=1 -fPIC"
export INSTRUMENT_FLAGS="${DEFINES_FLAGS} ${LLVM_SANITIZE}"
;;
*zcash-wrapper)
echo -n "Call this script instead of your regular compiler, and if the absolute "
echo -n "path of the CWD the wrapper was called from matches a regex in the "
echo -n "array 'dirs_to_instrument', it will instrument the resulting object. "
echo -n "Otherwise it will exec directly to the original compiler without "
echo "changing arguments."
echo -n "You can also set LLVM_SANITIZE to whatever sanitizers you'd like to use."
echo -n "the default is 'address'. You don't need the -fsanitize=fuzzer part, "
echo "this script handles that"
exit
;;
esac
# Check if we should instrument
for i in "${dirs_to_instrument[@]}"
do
if echo -- "`pwd`" | grep "$i"; then
# We found a match, let's instrument this one.
echo "pwd (`pwd`) matches dirs_to_instrument array element ($i). Adding instrumentation flags..." | log
echo "command with defines and instrumentation added:" | color=$green log
echo -- "$COMPILER" $INSTRUMENT_FLAGS "$@" | log
exec -- "$COMPILER" $INSTRUMENT_FLAGS "$@"
fi
done
# No match, just pass-through.
echo -e -- "${red}command with defines added:${normal}" | color=$red log
echo -- "$COMPILER" $DEFINES_FLAGS "$@" | log
exec -- "$COMPILER" $DEFINES_FLAGS "$@"

View File

@ -0,0 +1 @@
zcash-wrapper

View File

@ -0,0 +1 @@
zcash-wrapper