Browse Source

build: introduce a fuzzing mode

This adds a new configuration, --enable-fuzzing (which is more than
welcome to be coupled with --enable-address-sanitizer), to pass the
fuzzer sanitizer argument when compiling objects. This allows libfuzzer
to actually be able "to fuzz" by detecting coverage and be smart when
mutating inputs.

As libfuzzer brings its own ~~fees~~ main(), we compile objects with
fsanitize=fuzzer-no-link, and special-case the linkage of the fuzz
targets.

A "lib" is added to abstract out the interface to the fuzzing tool used.
This allow us to use the same targets to fuzz using AFL, hongfuzz or w/e
by adding their entrypoints into libfuzz. (h/t to practicalswift who
introduced this for bitcoin-core, which i mimiced)

Signed-off-by: Antoine Poinsot <darosior@protonmail.com>
travis-experimental
Antoine Poinsot 5 years ago
committed by Christian Decker
parent
commit
62b54d0125
  1. 30
      Makefile
  2. 17
      configure
  3. 30
      tests/fuzz/Makefile
  4. 22
      tests/fuzz/fuzz-addr.c
  5. 16
      tests/fuzz/libfuzz.c
  6. 18
      tests/fuzz/libfuzz.h

30
Makefile

@ -42,10 +42,14 @@ VG=VALGRIND=1 valgrind -q --error-exitcode=7
VG_TEST_ARGS = --track-origins=yes --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all VG_TEST_ARGS = --track-origins=yes --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all
endif endif
SANITIZER_FLAGS :=
ifneq ($(ASAN),0) ifneq ($(ASAN),0)
SANITIZER_FLAGS=-fsanitize=address SANITIZER_FLAGS += -fsanitize=address
else endif
SANITIZER_FLAGS=
ifneq ($(FUZZING), 0)
SANITIZER_FLAGS += -fsanitize=fuzzer-no-link
endif endif
ifeq ($(DEVELOPER),1) ifeq ($(DEVELOPER),1)
@ -208,6 +212,7 @@ WIRE_GEN_DEPS := $(WIRE_GEN) $(wildcard tools/gen/*_template)
# These are filled by individual Makefiles # These are filled by individual Makefiles
ALL_PROGRAMS := ALL_PROGRAMS :=
ALL_TEST_PROGRAMS := ALL_TEST_PROGRAMS :=
ALL_FUZZ_TARGETS :=
ALL_C_SOURCES := ALL_C_SOURCES :=
ALL_C_HEADERS := gen_header_versions.h gen_version.h ALL_C_HEADERS := gen_header_versions.h gen_version.h
@ -224,6 +229,8 @@ unexport CFLAGS
CONFIGURATOR_CC := $(CC) CONFIGURATOR_CC := $(CC)
LDFLAGS += $(PIE_LDFLAGS) $(SANITIZER_FLAGS) $(COPTFLAGS) LDFLAGS += $(PIE_LDFLAGS) $(SANITIZER_FLAGS) $(COPTFLAGS)
CFLAGS += $(SANITIZER_FLAGS)
ifeq ($(STATIC),1) ifeq ($(STATIC),1)
# For MacOS, Jacob Rapoport <jacob@rumblemonkey.com> changed this to: # For MacOS, Jacob Rapoport <jacob@rumblemonkey.com> changed this to:
# -L/usr/local/lib -Wl,-lgmp -lsqlite3 -lz -Wl,-lm -lpthread -ldl $(COVFLAGS) # -L/usr/local/lib -Wl,-lgmp -lsqlite3 -lz -Wl,-lm -lpthread -ldl $(COVFLAGS)
@ -308,6 +315,9 @@ include devtools/Makefile
include tools/Makefile include tools/Makefile
include plugins/Makefile include plugins/Makefile
include tests/plugins/Makefile include tests/plugins/Makefile
ifneq ($(FUZZING),0)
include tests/fuzz/Makefile
endif
# We make pretty much everything depend on these. # We make pretty much everything depend on these.
ALL_GEN_HEADERS := $(filter gen%.h %printgen.h %wiregen.h,$(ALL_C_HEADERS)) ALL_GEN_HEADERS := $(filter gen%.h %printgen.h %wiregen.h,$(ALL_C_HEADERS))
@ -473,10 +483,10 @@ gen_header_versions.h: tools/headerversions
@tools/headerversions $@ @tools/headerversions $@
# All binaries require the external libs, ccan and system library versions. # All binaries require the external libs, ccan and system library versions.
$(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS): $(EXTERNAL_LIBS) $(CCAN_OBJS) $(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS): $(EXTERNAL_LIBS) $(CCAN_OBJS)
# Each test program depends on its own object. # Each test program depends on its own object.
$(ALL_TEST_PROGRAMS): %: %.o $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS): %: %.o
# Without this rule, the (built-in) link line contains # Without this rule, the (built-in) link line contains
# external/libwallycore.a directly, which causes a symbol clash (it # external/libwallycore.a directly, which causes a symbol clash (it
@ -485,6 +495,13 @@ $(ALL_TEST_PROGRAMS): %: %.o
$(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS): $(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS):
@$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) -o $@) @$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) -o $@)
# We special case the fuzzing target binaries, as they need to link against libfuzzer,
# which brings its own main().
FUZZ_LDFLAGS = -fsanitize=fuzzer
$(ALL_FUZZ_TARGETS):
@$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) $(FUZZ_LDFLAGS) -o $@)
# Everything depends on the CCAN headers, and Makefile # Everything depends on the CCAN headers, and Makefile
$(CCAN_OBJS) $(CDUMP_OBJS): $(CCAN_HEADERS) Makefile $(CCAN_OBJS) $(CDUMP_OBJS): $(CCAN_HEADERS) Makefile
@ -504,7 +521,7 @@ update-ccan:
# Now ALL_PROGRAMS is fully populated, we can expand it. # Now ALL_PROGRAMS is fully populated, we can expand it.
all-programs: $(ALL_PROGRAMS) all-programs: $(ALL_PROGRAMS)
all-test-programs: $(ALL_TEST_PROGRAMS) all-test-programs: $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS)
distclean: clean distclean: clean
$(RM) ccan/config.h config.vars $(RM) ccan/config.h config.vars
@ -518,6 +535,7 @@ clean:
$(RM) $(CCAN_OBJS) $(CDUMP_OBJS) $(ALL_OBJS) $(RM) $(CCAN_OBJS) $(CDUMP_OBJS) $(ALL_OBJS)
$(RM) $(ALL_PROGRAMS) $(RM) $(ALL_PROGRAMS)
$(RM) $(ALL_TEST_PROGRAMS) $(RM) $(ALL_TEST_PROGRAMS)
$(RM) $(ALL_FUZZ_TARGETS)
$(RM) gen_*.h */gen_* ccan/tools/configurator/configurator $(RM) gen_*.h */gen_* ccan/tools/configurator/configurator
$(RM) ccan/ccan/cdump/tools/cdump-enumstr.o $(RM) ccan/ccan/cdump/tools/cdump-enumstr.o
find . -name '*gcda' -delete find . -name '*gcda' -delete

17
configure

@ -120,6 +120,7 @@ set_defaults()
CONFIGURATOR_CC=${CONFIGURATOR_CC-$CC} CONFIGURATOR_CC=${CONFIGURATOR_CC-$CC}
VALGRIND=${VALGRIND:-$(default_valgrind_setting)} VALGRIND=${VALGRIND:-$(default_valgrind_setting)}
TEST_NETWORK=${TEST_NETWORK:-regtest} TEST_NETWORK=${TEST_NETWORK:-regtest}
FUZZING=${FUZZING:-0}
} }
usage() usage()
@ -155,6 +156,7 @@ usage()
echo " Static link sqlite3, gmp and zlib libraries" echo " Static link sqlite3, gmp and zlib libraries"
usage_with_default "--enable/disable-address-sanitizer" "$ASAN" "enable" "disable" usage_with_default "--enable/disable-address-sanitizer" "$ASAN" "enable" "disable"
echo " Compile with address-sanitizer" echo " Compile with address-sanitizer"
usage_with_default "--enable/disable-fuzzing" "$FUZZING" "enable" "disable"
exit 1 exit 1
} }
@ -206,6 +208,8 @@ for opt in "$@"; do
--disable-static) STATIC=0;; --disable-static) STATIC=0;;
--enable-address-sanitizer) ASAN=1;; --enable-address-sanitizer) ASAN=1;;
--disable-address-sanitizer) ASAN=0;; --disable-address-sanitizer) ASAN=0;;
--enable-fuzzing) FUZZING=1;;
--disable-fuzzing) FUZZING=0;;
--help|-h) usage;; --help|-h) usage;;
*) *)
echo "Unknown option '$opt'" >&2 echo "Unknown option '$opt'" >&2
@ -229,6 +233,18 @@ if [ "$ASAN" = "1" ]; then
fi fi
fi fi
if [ "$FUZZING" = "1" ]; then
case "$CC" in
(*"clang"*)
;;
(*)
echo "Fuzzing is currently only supported with clang."
exit 1
;;
esac
fi
SQLITE3_CFLAGS="" SQLITE3_CFLAGS=""
SQLITE3_LDLIBS="-lsqlite3" SQLITE3_LDLIBS="-lsqlite3"
if command -v "${PKG_CONFIG}" >/dev/null; then if command -v "${PKG_CONFIG}" >/dev/null; then
@ -400,6 +416,7 @@ add_var ASAN "$ASAN"
add_var TEST_NETWORK "$TEST_NETWORK" add_var TEST_NETWORK "$TEST_NETWORK"
add_var HAVE_PYTHON3_MAKO "$HAVE_PYTHON3_MAKO" add_var HAVE_PYTHON3_MAKO "$HAVE_PYTHON3_MAKO"
add_var SHA256SUM "$SHA256SUM" add_var SHA256SUM "$SHA256SUM"
add_var FUZZING "$FUZZING"
# Hack to avoid sha256 name clash with libwally: will be fixed when that # Hack to avoid sha256 name clash with libwally: will be fixed when that
# becomes a standalone shared lib. # becomes a standalone shared lib.

30
tests/fuzz/Makefile

@ -0,0 +1,30 @@
LIBFUZZ_SRC := tests/fuzz/libfuzz.c
LIBFUZZ_HEADERS := $(LIBFUZZ_SRC:.c=.h)
LIBFUZZ_OBJS := $(LIBFUZZ_SRC:.c=.o)
FUZZ_TARGETS_SRC := $(wildcard tests/fuzz/fuzz-*.c)
FUZZ_TARGETS_OBJS := $(FUZZ_TARGETS_SRC:.c=.o)
FUZZ_TARGETS_BIN := $(FUZZ_TARGETS_SRC:.c=)
FUZZ_COMMON_OBJS := \
common/utils.o
$(FUZZ_TARGETS_OBJS): $(COMMON_HEADERS) $(WIRE_HEADERS) $(COMMON_SRC)
$(FUZZ_TARGETS_BIN): $(LIBFUZZ_OBJS) $(FUZZ_COMMON_OBJS) $(BITCOIN_OBJS)
tests/fuzz/fuzz-addr: \
common/amount.o \
common/addr.o \
common/base32.o \
common/bech32.o \
common/bigsize.o \
common/json.o \
common/json_stream.o \
common/wireaddr.o \
common/type_to_string.o \
wire/fromwire.o \
wire/onion_wiregen.o \
wire/towire.o
ALL_C_SOURCES += $(FUZZ_TARGETS_SRC) $(LIBFUZZ_SRC)
ALL_FUZZ_TARGETS += $(FUZZ_TARGETS_BIN)

22
tests/fuzz/fuzz-addr.c

@ -0,0 +1,22 @@
#include "common/utils.h"
#include <stdint.h>
#include <tests/fuzz/libfuzz.h>
#include <ccan/ccan/tal/tal.h>
#include <common/addr.h>
#include <common/setup.h>
void init(int *argc, char ***argv)
{
chainparams = chainparams_for_network("bitcoin");
common_setup("fuzzer");
}
void run(const uint8_t *data, size_t size)
{
uint8_t *script_pubkey = tal_dup_arr(tmpctx, uint8_t, data, size, 0);
encode_scriptpubkey_to_addr(tmpctx, chainparams, script_pubkey);
clean_tmpctx();
}

16
tests/fuzz/libfuzz.c

@ -0,0 +1,16 @@
#include <tests/fuzz/libfuzz.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
int LLVMFuzzerInitialize(int *argc, char ***argv);
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
run(data, size);
return 0;
}
int LLVMFuzzerInitialize(int *argc, char ***argv) {
init(argc, argv);
return 0;
}

18
tests/fuzz/libfuzz.h

@ -0,0 +1,18 @@
#ifndef LIGHTNING_TESTS_FUZZ_LIBFUZZ_H
#define LIGHTNING_TESTS_FUZZ_LIBFUZZ_H
#include <stddef.h>
#include <stdint.h>
/* Called once before running the target. Use it to setup the testing
* environment. */
void init(int *argc, char ***argv);
/* The actual target called multiple times with mutated data. */
void run(const uint8_t *data, size_t size);
/* Copy an array of chunks from data. */
const uint8_t **get_chunks(const void *ctx, const uint8_t *data,
size_t data_size, size_t chunk_size);
#endif /* LIGHTNING_TESTS_FUZZ_LIBFUZZ_H */
Loading…
Cancel
Save