Auto merge of #4171 - defuse:fuzzer-packaging, r=str4d

Add AFL in zcutil (with all-in-one script)

Supersedes #4156 and #4167.

Fuzzing targets and input sets are defined by the contents of directories in `./src/fuzzing/`. Inside the directory, there's a `fuzz.cpp` and `fuzz.h` with a `main()` function that will replace `zcashd`'s actual `main()` as well as an `input` subdirectory containing the inputs, one per file. To just run a fuzzer, you can, for example...

```
make clean # if you've previously build zcashd without AFL instrumentation
./zcutil/afl/afl-getbuildrun.sh DecodeHexTx
```

Alternatively you can...

```
./zcutil/afl/afl-get.sh /tmp/afl   # (or wherever you want to build AFL)
./zcutil/afl/afl-build.sh /tmp/afl DecodeHexTx -j$(nproc)
./zcutil/afl/afl-run.sh /tmp/afl DecodeHexTx
```

Run `make clean` whenever you switch between a normal build and an AFL-instrumented build.
This commit is contained in:
Homu 2019-11-06 03:33:05 -08:00
commit 5ec69e8c2c
21 changed files with 193 additions and 1 deletions

3
.gitignore vendored
View File

@ -109,3 +109,6 @@ libzcashconsensus.pc
contrib/debian/files
contrib/debian/substvars
src/fuzzing/*/output
src/fuzz.cpp

View File

@ -107,6 +107,12 @@ AC_ARG_ENABLE(tests,
[use_tests=$enableval],
[use_tests=yes])
AC_ARG_ENABLE([fuzz-main],
[AS_HELP_STRING([--enable-fuzz-main],
[Replace main() with a stub for fuzzing (default is no)])],
[use_fuzz_main=$enableval],
[use_fuzz_main=no])
AC_ARG_ENABLE([asan],
[AS_HELP_STRING([--enable-asan],
[instrument the executables with asan (default is no)])],
@ -447,6 +453,11 @@ if test x$TARGET_OS != xwindows; then
AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"])
fi
if test x$use_fuzz_main == xyes; then
CFLAGS="$CFLAGS -DZCASH_FUZZ=1"
CXXFLAGS="$CXXFLAGS -DZCASH_FUZZ=1"
fi
#asan and tsan cannot be used together
if test x$use_asan$use_tsan == xyesyes; then
AC_MSG_ERROR(asan and tsan cannot be simultaneously enabled)

View File

@ -564,6 +564,7 @@ clean-local:
-$(MAKE) -C secp256k1 clean
-$(MAKE) -C univalue clean
rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno
rm -f fuzz.cpp
-rm -f config.h
.rc.o:

View File

@ -186,7 +186,10 @@ bool AppInit(int argc, char* argv[])
return fRet;
}
#ifdef ZCASH_FUZZ
#warning BUILDING A FUZZER, NOT THE REAL MAIN
#include "fuzz.cpp"
#else
int main(int argc, char* argv[])
{
SetupEnvironment();
@ -196,3 +199,4 @@ int main(int argc, char* argv[])
return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
#endif

View File

@ -0,0 +1,17 @@
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
bool fuzz_DecodeHexTxFunction (const std::string& strHexTx) {
CTransaction tx;
return DecodeHexTx(tx, strHexTx);
}
int fuzz_DecodeHexTx (int argc, char *argv[]) {
std::ifstream t(argv[1]);
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
if (fuzz_DecodeHexTxFunction (str)) { fprintf(stdout, "Decoded hex string") ; return 0; }
else { fprintf(stderr, "Could not decode hex string") ; return -1; }
}
int main (int argc, char *argv[]) { return fuzz_DecodeHexTx(argc, argv); }

View File

@ -0,0 +1 @@
0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff2a03677b090004e161a75d0edb51e814ae3737f790e3c94f11495327270fdfbd6132d5e90f87507455844cffffffff023f1e9b3b000000001976a914e0082ee3d89f84adbadaaebddd1746bb20e6d4ee88ac80b2e60e0000000017a914ce811a7457b9f4553af120e553bc8ddd8bb33ce98700000000000000000000000000000000000000

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
0400008085202f89000000000000ec8a0900102700000000000001251409d9698abef6527f651bac9cfd1eb57b7ff934b05e6921879e5fae0678adf9792e02ab24556f3fc6a24519d24ae7f2b8e3aed982cf3e36ea1a0c1243600d953fae63415e818e050048edb6fed27cc3f893d1b163620270bd7d7ce2065f303f57c1d621d3336305bcf9ffb8c13c917fcbcc05b68c27c77abf90a1a7013e1791fe0bfc4c147a66c8cf87a3ede59ba56f585bb6301eb3e0952fe066a0e40cfb8a58c1ed227471e9eafb999169ff7e19a85c3e9c739f7fe974c849309c379c9e3d6ed2d913ddaee302e6c54e929a85193ae7a7eb327fbae9082945732dfbd14a0b1cf66446b20c7e054e7cbd4a428eded695035e5d7144efb73b9e4ce10d2ce23516e95bbb3971403460ced0ac269eb2b86142ddb53d83ef09c6673cdba495f23dc0e1d17317c8debb31d0963c45884d2af79448057084b23a1776feacb8109246d49dc988b3fa7f32d53dbc3c40e5a0932dad6a87c916b6bbbec2d803bd806424ab3374eab7880e69570c6e94495e6c7445ad3911a29bfdf1f702688a66180902a001d887b84e87a1a08147b3ec53b92f7f3f214a1a1c60cded3d7b9e1516033a2586a074ad5b01a178db42b76d16c3db5302d62825347390cf5b760ea2362a27db3063d4a2021514a3c64e3bca0896a6b52c0b8ba4ef9120eb7cf9367ad6ca859d1986b00789a7889d2caf7b016a2e000bd0538359b250871497686c12321941787b6a65364138c7484037e7143d83a6d2606722b27ce75426cb1392e92ae4470dd8aa04f0fdeca4a924c599822496689bd7388214e68c8a94a9e07f0a41d485ff7cdce14fb1442f017231b0d27b7992b17007d034b0df7cb373495fd99668a4be34ceeea5b3a425cda44a1bb726e1cf0ac6b9cd05da93482524bdc60aec7300ae09281d5c62325d3c9ada64c41802358a692fb219712ff1e872476806d690b7f2ff68b023df2942d220a98b09af78dc85d1c1f4e39cc01aa4d4d7892fd665e1c0ec2fc35724586c75a9ff3c5f2d27226b8013562b59a6ee69d4d36e9d858da5a41a32c9769c41e7362681b5c93b5bb915adf1d08b71e2f74f15407dc2f3f48d7043d619d22f613714f21b481a80113a4a2b9616325aa52cf4cc6dd8db54f764ae7c7270476406ee8313b66e1109c14cb4ccd138b41e6c9e8005c593ab6e40a5d6bf5017a52faec7d0fdc3adf600af499d0e3c73a5567bbd2bd366132e2b222095843898814865e3b3a2e8662e87f9618764eb4b962e5eb8e8a7740197365eb2d0c086b4ffd65bd19cec6dba33634eefba2353ac622985f3e4dd6df772691661d58de3297d440ea58edc6297e877790e56320d13adfafa71b79b7381366564befbb40470b254935bb83a9acf4124cc45993c99c1713f500dad49bb3ba6ce70dfbb75091790a0a033a9c3dd0d8975fd005a0c5b9d3ca230c36173c96dc3d41833d3b75ef392edc08c5343f838f8d19714a5ce6afaafe34914a65177e348d56133ec9d1fdec768a4b4e3221d7820f941c0e79f5a97d25ca99a02e3211fd4d74b926cf14fad605fcd8631b2a9ce8156703e9623a1f6ef50e039e71ae0560a9b96a2965c964b6b133d8b3ee35e38f91c18645fab67b798a287fd50c9d48f39b213a1904b8a62102c214a0692ad01f5f74df6ff639cd28d95e720842295cd337d9639dd2e90f8996fcad10911eac82e8aa060ae211ad4620c1c6cc20b277f5b1f45b906625d98f07dfd16b5b70b983cd881571a6af39012ec30c43b8b1e12c3534d852453b26f8c2c50c4a902a339d67a0dc9081668a383ae6f28c0f2c7eb3c9cc0597d16c1ba83062729e1c90407431caf00f46e04c3350277bc318d2cc5d8d9857d294d50729b72dc3b78725a8ad6a40ffcae066d91108ebb9d34a6bfee2bb41b56fc7f08bf4b50e8c52567509215948f414356e5639cfadeceb04fd7d8b3e151fa3ee77f7ba87dbc0337b81e240e03508c69d35d1e5e3d998a589443edb68059b87e39b241cb16e09b6f1da1b381cd467f85df6b1cb29e7d8a89bf6216fdf927ede71ee945c36f313278ca50618da3d160fce0da32b325f27be0afaaeb92856d4dea26dba9922f5c541e76453893bea8f4ad4aed716835fa6ee6a04453d5fa1efe6efb64e060a82406c4230c8ed981f45f4ab078864b4388f3866aee9f76d2b0e2bea8601b4f8e79245fc5c94995be9674c1ae064242f6a65fad2ef9ea28e6bcba11fa07f14fad1c9da5f9cc78b4bb7217015968122f9c9ea59b661c3188c909e76caffd48185f6917ee306be440b67f7712500c5d37d14481e3ea01d78aeeca4178bb9835627d1d799bd5514039ac1f4bde04315bbfa194900885b9718be9871d629b9df14a022821c83c3039aec07f32575d2e84f6c3acc90d02a26a6380d63012310f6abd49c746cec06eb324715a2fa74d6c8e8327c85cabcbd78ece00fcff64cc36822b3ed28f15cc85fd4945e4010192890f30df28b6de4d3354e40e77121647a224e35874175a2de9cabca4dcead549d697a3bb76c0a2280c2a459ba63a9625ff53ce8accddd9b3dabb4a285d4cac4d410e787f5e8b9f1ba509d28fd95a2e0d3400d1c5e27394a64324ed44884eed42b273375bccc8b7df8bc035172eea0198b9ac8c1c99ecbe547102ec8449cc0d436f85567247cbb2942a51abbce1714caeb852afbd4a64649c2655d7b577061591b6a928c88f679a3f914e5608cc400c027e78911c192704f260cf4e05b96c5df0579a22e591e28c9c344320c12088fff7e81e4aef56ad417a2782dc03dc1d94909f0cb2a70304b2e6cabe4165ee53893dd63b58a08968fa43a03851da5151c7609715c3f6d21f01d15b578d8c37426da5008801107e35f62525734cd0a7876f065b920e2ec461c4f3700b792cf89c406893949a1058cf422837ffe1f87209212cc379a3b482e15e8993b468d94565317917870c734555a61db272a24fe52e40479c9a25276a0cc2e0c42404d11e9e1aae1c7545a6f99cbcb2cc2dfc11e889eade5df7f7da7aa38f12af116d217af57f8e3ae575e69190676502088cba9295f775538dfe653eecd0cf504643c2ff6920719d0d83047e16052016459f7fc802b420f0796c771b7d988fd95a42b17f363a692346455e1c6e173298aafeea3fcfca393349ebbefa6899777264b5d0452aa8f70573ae688d9916861447da00d9d847eda7e34f88988086d6734bb43ec53b4a2d9f260103cde700415214614727f24927a7e49fa191153a41eb24e73f78aaf544c10d68fbe459e232be1af6b48fa01b2d018774e28483d2f990d0eae272680f57e5980ff2033907

View File

@ -0,0 +1 @@
0400008085202f89000000000000ed8a090000000000000000000000010000000000000000102700000000000046e5b5ba294641c0491ef8cb5a97591fe1fcee2bab2301553f9bc0f9acc9bcb70f836c9945bfbae86faf169fd9be228f6c373e0b029fd648e7d0660d8412b50c0597c61387abfc9f1122179f1c612ad72ce09cf6dd8576f60494d0a828c10706f7594b5f237eeec5fd0474f6a16f3923a6e6881a0fe790a5328813abb559dcba268fbbe6e428783271ce752e1b21bedd8b70d7c7ca741651853bcc411ff5099d7cb3dee3ee072fd53bc32b597f0deff17ceb2cd47448bf1432f5bfdc022435060208d9cbd0f9af4dd89bbd62cb05ad865c3bd6b691ef5fa04f5498d32004874acf51385eea2e98e23f9d4b440b00bdbd972f049c559b4934bbbb429029adf8892c7f3c1a86d5d2be4db51cfe098ea31451ea6965fe49e06ada1c6734c3d180068816e8c85e02ecb55f6a4e558590c52b04c768f5d22682b7eb2cfc18c308542e67b85a4831130409b5ae2cb15c18873ab269b3560f1a4d09d723f82dcd4051db4fcaefbfcf923fcfb35befc8777a7afbf7a4ffbee41aeb53172e1b67efbaad4e12e75a08e3bd85b16af909d3262e3d43085a8f54dd8c00047394265d0368323e82bc345da1477026f3dbc5e05d85723ea77b4dd7b98dbf6bbe7c8988eae224ece369106513fc1b3604b6cfb5ac49da226e6269a52d6aa30a3ae70d06dfce27cb497083568cf4e362be2417328107ed01fbf63e1667ba221ee89c4fa37e9460eafcbb15f6f9184c09fff97ba4ca183b46246d354ef47f4423dbbda454738effb3efe0cc0cd7381d73e0d768bb1e7056fce119bf4877ca07c2e959027285c3a24f3b58b8ff31acc3e30f5c09ccdfad7b762ce5e7388d15f154db726a68b8a0f4b9bdb943d9fd9aef8c1a4c7d89709e5b931804d6b32dcc42fc0790c77e0db3c86623f89cc961ad3a41a80a42a5c1700ff2e6c70ce03fdbe498beb8184bb3a2ad5eb806d349fdfbd6bd066133977790df768abfcd02806d33bd5507ec279f5cea71734d9990e3cebee3e2e631caa21253f20a75b5e59f626f4f5d0da425e4387d73f4ab7179b753f396583f39b6b849c0bde5179c15b82d459d1d06e0d929a0b5d6e8d51453b12edc59c402bdcd79872b7a3e53f67ec37c2ad9a024668f62e1c0def0f8b1d11836fc20fe732327721a0b061466240b7fd5de687c01f08f186da67b867767b401395acefa618c012d72f0cee463e0eb73ac8a8b94fdbcb759afc4b337c47eb65a4105986dc82fdd45e65f9fb4a131eaa59257e2e4d28b7463575d9e641df173cfebf32a3cecee2e77d1b46b317b6d55e5c3103893a019d340ee5e91d55365f70ad36eb6d7fed94a052187d856231873df88093d10b4724a635372b4bf0232a88b9d7d3b7681a523d2f8d82a921c6bcd687cdf2aa19aecc202b87fb9fcd1997b73afde048c69e7c6b5c122435bd5df8e6b197042d0e6ab2770551f0cdaf008cbf5a25468aca996d8c13f3de549afbacc363b55e94ee10c1bde4c7d7a55bc014935b65267de17ffe5592cc5ce641a2de2a303cc557bd829a19651971eeac586654b282f92486c59dc8ef335c6a9ecfb178c3e0d6ea6df5c2e84604ca89b2fe5902a26ac3274be48259fcabf0dde398c2821cfedd1c93cb05e9a9cb7dd3107cbac001af08f0a64727447e17d6cf5678dcac770dfe7af08de1e1f16dfbe4df37675a54b0a9c2cdb32b2eaa54850b9e338139974f49e2b3c02646529170e69622d64f300cda3903ea6a83923de195ef2b69528ffcbf182a0297c0ee5e11916e60190981625e819739226f68b924237532175d9b05a6859a92dd78e7abcca7cfc10497857a12f07e5d0fcad158ed0b9b76663902c6508a9f51de19204536de5830a497fa9a75d8a0aa610c868960fa868a45afa466fd70834864b6b85f80ac6ee226920b702415579e8fe1129e60a35dd3dee6ba67a2151c027137ce7ce7c296678360463408fc6f02ea00333d2fec8323fe4cfe2a70c4d4cfb169b5954e31c575474d29d4167a6d3a4fc2ba5a8963aec24e775056c34d7b5ad1a9287f22d55b1d7d22e72ae79c9d55ab4ca67dd05c63e2c0cde5342b79a9d838106b416ce9a858fb8cdfa30174c18314485fe46d2151bc94f43b99544998c127fe504919cc9d9c105544c72b7265314a35dc38e04b282301cd4780352a978fadb080d8d2493ae0b92493be110700f943e63de82e68e129e894a2182a2fcfac30f770ce4f0601f57dc22c01330d409349b4c44b4a627dfc74b8a5c6a066a032fc93c7c7ee1aaeb17a7bfaefc5197e4355a4bd41cdd8ac651fae7d4d2d1c0713ffe1399199275c27da96ca6f959550c1040d46364c7f65fe3af541a43042dae394be74bef1515e25ce1c1d126295b44dc05dbc4a658d076ca39f35504966aa8872008447f38f9259099d0056b0c057ce9926ac208984fb6875b3c1f440e37a1ab9c6cb0957e39972596db5e3eb10252434bff0738aa107152cd4184329ee63dd7cdfdcbb3a3548e3c5431cae2c2ca85f1cd30f2aeeedd04

View File

@ -0,0 +1,21 @@
bool fuzz_TxDeserializeFunction (const std::vector<unsigned char> txData) {
CTransaction tx;
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> tx;
return true;
} catch (const std::exception&) {
return false;
}
}
int fuzz_TxDeserialize (int argc, char *argv[]) {
std::ifstream t(argv[1]);
std::vector<unsigned char> vec((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
if (fuzz_TxDeserializeFunction (vec)) { fprintf(stdout, "Deserialized the transaction.") ; return 0; }
else { fprintf(stderr, "Could not deserialize the transaction.") ; return -1; }
}
int main (int argc, char *argv[]) { return fuzz_TxDeserialize(argc, argv); }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
zcutil/afl/afl-build.sh Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
# A wrapper around ./zcutil/build.sh for instrumenting the build with AFL:
# ./zcutil/afl/afl-build.sh <directory where AFL is installed> <fuzz case>
# You may obtain a copy of AFL using ./zcutil/afl/afl-get.sh.
set -eu -o pipefail
export AFL_INSTALL_DIR=$(realpath "$1")
FUZZ_CASE="$2"
shift 2
export AFL_LOG_DIR="$(pwd)"
export ZCUTIL=$(realpath "./zcutil")
cp "./src/fuzzing/$FUZZ_CASE/fuzz.cpp" src/fuzz.cpp
CONFIGURE_FLAGS="--enable-tests=no --enable-fuzz-main" "$ZCUTIL/build.sh" "CC=$ZCUTIL/afl/zcash-wrapper-gcc" "CXX=$ZCUTIL/afl/zcash-wrapper-g++" AFL_HARDEN=1 "$@"
echo "You can now run AFL as follows:"
echo "$ ./zcutil/afl/afl-run.sh '$AFL_INSTALL_DIR' '$FUZZ_CASE'"

33
zcutil/afl/afl-get.sh Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Obtains and builds a copy of AFL from source.
# ./zcutil/afl/afl-get.sh <directory to build and install AFL in>
set -eu -o pipefail
mkdir -p "$1"
cd "$1"
if [ ! -z "$(ls -A .)" ]; then
echo "$1 is not empty. This script will only attempt to build AFL in an empty directory."
exit 1
fi
# Get the AFL source
rm -f afl-latest.tgz
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
sha256sum afl-latest.tgz | grep '43614b4b91c014d39ef086c5cc84ff5f068010c264c2c05bf199df60898ce045'
if [ "$?" != "0" ]
then
echo "Wrong SHA256 hash for afl"
exit
fi
tar xvf afl-latest.tgz
mv afl-*/* .
# Build AFL
make
echo "You can now build zcashd with AFL instrumentation as follows:"
echo "$ make clean # if you've already built zcashd without AFL instrumentation"
echo "$ ./zcutil/afl/afl-build.sh '$(pwd)' <fuzz case> -j\$(nproc)"
echo "...where <fuzz case> is the name of a directory in src/fuzzing."

20
zcutil/afl/afl-getbuildrun.sh Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Builds AFL and an instrumented zcashd, then begins fuzzing.
# This script must be run from within the top level directory of a zcash clone.
# Pass it the name of a directory in ./src/fuzzing.
# Additional arguments are passed-through to AFL.
set -eu -o pipefail
FUZZ_CASE="$1"
shift 1
export AFL_INSTALL_DIR=$(realpath "./afl-temp")
if [ ! -d "$AFL_INSTALL_DIR" ]; then
mkdir "$AFL_INSTALL_DIR"
./zcutil/afl/afl-get.sh "$AFL_INSTALL_DIR"
fi
./zcutil/afl/afl-build.sh "$AFL_INSTALL_DIR" "$FUZZ_CASE" -j$(nproc)
./zcutil/afl/afl-run.sh "$AFL_INSTALL_DIR" "$FUZZ_CASE" "$@"

9
zcutil/afl/afl-run.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -eu -o pipefail
AFL_INSTALL_DIR="$1"
FUZZ_CASE="$2"
shift 2
"$AFL_INSTALL_DIR/afl-fuzz" -i "./src/fuzzing/$FUZZ_CASE/input" -o "./src/fuzzing/$FUZZ_CASE/output" "$@" ./src/zcashd @@

48
zcutil/afl/zcash-wrapper Executable file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -ex -o pipefail
export ARGS=$@
instrument=(
"\/src$"
)
if [ "$override_instrument" != "" ]
then
instrument = $override_instrument
fi
# Store the command line we were given to a file
(echo "$ARGS" ; pwd) >> "$AFL_LOG_DIR/zcash-build-wrapper.log"
# Work out which compiler we were called as
case $0 in
*zcash-wrapper-g++)
COMPILER="g++"
;;
*zcash-wrapper-gcc)
COMPILER="gcc"
;;
*zcash-wrapper)
echo "Call this script instead of your regular compiler, and if the absolute path of the CWD the wrapper was called from matches a regex in the array 'instrument', it will call AFL to instrument the resulting binary. Otherwise it will call either g++ or gcc depending on how it was invoked. \$AFL_INSTALL_DIR must be set to the path where AFL is installed."
exit
;;
esac
# Check if we should instrument
for i in "${instrument[@]}"
do
if echo -- "`pwd`" | grep "$i"; then
# We found a match, let's instrument this one.
echo "Matched directory `pwd` to instrument element $i. Instrumenting this call." >> "$AFL_LOG_DIR/zcash-build-wrapper.log"
exec -- "$AFL_INSTALL_DIR/afl-$COMPILER" "$@"
fi
done
# No match, just pass-through.
exec -- "$COMPILER" "$@"

View File

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

View File

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