diff --git a/Makefile b/Makefile index f665f8a27..3cee618f9 100644 --- a/Makefile +++ b/Makefile @@ -392,10 +392,7 @@ include cln-grpc/Makefile endif include plugins/Makefile include tests/plugins/Makefile - -ifneq ($(FUZZING),0) - include tests/fuzz/Makefile -endif +include tests/fuzz/Makefile ifneq ($V,1) MSGGEN_ARGS := -s @@ -709,6 +706,7 @@ endif # We special case the fuzzing target binaries, as they need to link against libfuzzer, # which brings its own main(). # FUZZER_LIB and LLVM_LDFLAGS are set by configure script on macOS +ifneq ($(FUZZING),0) ifeq ($(OS),Darwin) ifneq ($(FUZZER_LIB),) FUZZ_LDFLAGS = $(FUZZER_LIB) $(LLVM_LDFLAGS) @@ -718,9 +716,10 @@ endif else FUZZ_LDFLAGS = -fsanitize=fuzzer endif +endif $(ALL_FUZZ_TARGETS): - @$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) libccan.a $(FUZZ_LDFLAGS) -o $@) + @$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) libcommon.a libccan.a $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) $(FUZZ_LDFLAGS) -o $@) ifeq ($(OS),Darwin) @$(call VERBOSE, "dsymutil $@", dsymutil $@) endif @@ -841,6 +840,10 @@ update-mocks/%: % $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES) unittest/%: % bolt-precheck BOLTDIR=$(LOCAL_BOLTDIR) $(VG) $(VG_TEST_ARGS) $* > /dev/null +# FIXME: we don't do leak detection on fuzz tests, since they don't have a cleanup function. +fuzzunittest/%: % bolt-precheck + BOLTDIR=$(LOCAL_BOLTDIR) $(VG) $* > /dev/null + # Commands MKDIR_P = mkdir -p INSTALL = install diff --git a/common/peer_failed.h b/common/peer_failed.h index b782e525b..5be8ba8fb 100644 --- a/common/peer_failed.h +++ b/common/peer_failed.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include struct channel_id; struct per_peer_state; diff --git a/tests/fuzz/Makefile b/tests/fuzz/Makefile index ce2d39737..c2440e9eb 100644 --- a/tests/fuzz/Makefile +++ b/tests/fuzz/Makefile @@ -13,8 +13,13 @@ FUZZ_TARGETS_SRC := $(wildcard tests/fuzz/fuzz-*.c) FUZZ_TARGETS_OBJS := $(FUZZ_TARGETS_SRC:.c=.o) FUZZ_TARGETS_BIN := $(FUZZ_TARGETS_SRC:.c=) -$(FUZZ_TARGETS_OBJS): $(COMMON_HEADERS) $(WIRE_HEADERS) $(COMMON_SRC) +$(FUZZ_TARGETS_OBJS): $(COMMON_HEADERS) $(WIRE_HEADERS) $(COMMON_SRC) tests/fuzz/libfuzz.h $(FUZZ_TARGETS_BIN): $(LIBFUZZ_OBJS) libcommon.a ALL_C_SOURCES += $(FUZZ_TARGETS_SRC) $(LIBFUZZ_SRC) ALL_FUZZ_TARGETS += $(FUZZ_TARGETS_BIN) + +# In non-fuzzing builds, these become normal tests. +ifneq ($(FUZZING),1) +check-units: $(FUZZ_TARGETS_BIN:%=fuzzunittest/%) +endif diff --git a/tests/fuzz/bolt12.h b/tests/fuzz/bolt12.h index b5ef5a7fd..118924875 100644 --- a/tests/fuzz/bolt12.h +++ b/tests/fuzz/bolt12.h @@ -144,6 +144,11 @@ size_t LLVMFuzzerCustomCrossOver(const u8 *data1, size_t size1, const u8 *data2, return encoded_size; } -void init(int *argc, char ***argv) { common_setup("fuzzer"); } +void init(int *argc, char ***argv) +{ + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); +} #endif /* LIGHTNING_TESTS_FUZZ_BOLT12_H */ diff --git a/tests/fuzz/connectd_handshake.h b/tests/fuzz/connectd_handshake.h index 6412db8a9..6216e2359 100644 --- a/tests/fuzz/connectd_handshake.h +++ b/tests/fuzz/connectd_handshake.h @@ -74,7 +74,9 @@ void init(int *argc, char ***argv) assert(devnull >= 0); status_setup_sync(devnull); - common_setup("fuzzer"); + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); /* These keys are copied from BOLT 8 test vectors, though we use them in * a different setting. diff --git a/tests/fuzz/fuzz-addr.c b/tests/fuzz/fuzz-addr.c index 082c54931..a833bc298 100644 --- a/tests/fuzz/fuzz-addr.c +++ b/tests/fuzz/fuzz-addr.c @@ -9,7 +9,9 @@ void init(int *argc, char ***argv) { chainparams = chainparams_for_network("bitcoin"); - common_setup("fuzzer"); + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); } void run(const uint8_t *data, size_t size) diff --git a/tests/fuzz/fuzz-bolt11.c b/tests/fuzz/fuzz-bolt11.c index 5eeeeda2f..6f706e042 100644 --- a/tests/fuzz/fuzz-bolt11.c +++ b/tests/fuzz/fuzz-bolt11.c @@ -18,7 +18,12 @@ size_t LLVMFuzzerCustomCrossOver(const u8 *in1, size_t in1_size, const u8 *in2, size_t in2_size, u8 *out, size_t max_out_size, unsigned seed); -void init(int *argc, char ***argv) { common_setup("fuzzer"); } +void init(int *argc, char ***argv) +{ + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); +} // Encodes a dummy bolt11 invoice into `fuzz_data` and returns the size of the // encoded string. diff --git a/tests/fuzz/fuzz-bolt12-bech32-decode.c b/tests/fuzz/fuzz-bolt12-bech32-decode.c index bd3d86742..43f5fd219 100644 --- a/tests/fuzz/fuzz-bolt12-bech32-decode.c +++ b/tests/fuzz/fuzz-bolt12-bech32-decode.c @@ -8,7 +8,12 @@ /* Include bolt12.c directly, to gain access to string_to_data(). */ #include "../../common/bolt12.c" -void init(int *argc, char ***argv) { common_setup("fuzzer"); } +void init(int *argc, char ***argv) +{ + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); +} void run(const u8 *data, size_t size) { diff --git a/tests/fuzz/fuzz-channel_id.c b/tests/fuzz/fuzz-channel_id.c index df3d35e40..bf2c2d03b 100644 --- a/tests/fuzz/fuzz-channel_id.c +++ b/tests/fuzz/fuzz-channel_id.c @@ -10,7 +10,9 @@ void init(int *argc, char ***argv) { - common_setup("fuzzer"); + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); } void run(const uint8_t *data, size_t size) diff --git a/tests/fuzz/fuzz-close_tx.c b/tests/fuzz/fuzz-close_tx.c index 0fd29e5ac..d3f3abf98 100644 --- a/tests/fuzz/fuzz-close_tx.c +++ b/tests/fuzz/fuzz-close_tx.c @@ -12,7 +12,9 @@ void init(int *argc, char ***argv) { - common_setup("fuzzer"); + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); chainparams = chainparams_for_network("bitcoin"); } diff --git a/tests/fuzz/fuzz-codex32.c b/tests/fuzz/fuzz-codex32.c index 87e232423..eaaccc3f3 100644 --- a/tests/fuzz/fuzz-codex32.c +++ b/tests/fuzz/fuzz-codex32.c @@ -34,7 +34,9 @@ static struct codex32 *codex32_dup(const tal_t *ctx, const struct codex32 *src) void init(int *argc, char ***argv) { - common_setup("fuzzer"); + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); } /* Custom mutator with structure-aware and byte-level mutations */ diff --git a/tests/fuzz/fuzz-cryptomsg.c b/tests/fuzz/fuzz-cryptomsg.c index c7b7c6429..9b3a47f6d 100644 --- a/tests/fuzz/fuzz-cryptomsg.c +++ b/tests/fuzz/fuzz-cryptomsg.c @@ -54,7 +54,9 @@ void init(int *argc, char ***argv) init_cs_in.s_ck = ck; init_cs_in.r_ck = ck; - common_setup("fuzzer"); + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); } /* Test that encrypting and decrypting the message does not alter it. */ diff --git a/tests/fuzz/fuzz-initial_channel.c b/tests/fuzz/fuzz-initial_channel.c index 5549b36db..2fa04c26b 100644 --- a/tests/fuzz/fuzz-initial_channel.c +++ b/tests/fuzz/fuzz-initial_channel.c @@ -23,7 +23,9 @@ void init(int *argc, char ***argv) { - common_setup("fuzzer"); + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); int devnull = open("/dev/null", O_WRONLY); status_setup_sync(devnull); chainparams = chainparams_for_network("bitcoin"); diff --git a/tests/fuzz/libfuzz.c b/tests/fuzz/libfuzz.c index 98c5fd055..78ee0064a 100644 --- a/tests/fuzz/libfuzz.c +++ b/tests/fuzz/libfuzz.c @@ -2,10 +2,18 @@ #include #include +#include +#include +#include +#include #include +#include +#include #include #include +#include #include +#include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); int LLVMFuzzerInitialize(int *argc, char ***argv); @@ -118,3 +126,38 @@ size_t cross_over(const u8 *in1, size_t in1_size, const u8 *in2, max_out_size); return overwrite_part(in1, in1_size, in2, in2_size, out, max_out_size); } + +/* In non-fuzzing builds, these become unit tests which just run the corpora: + * this is also good for attaching a debugger to! */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +int main(int argc, char *argv[]) +{ + DIR *d; + struct dirent *di; + + common_setup(argv[0]); + assert(chdir("tests/fuzz/corpora") == 0); + assert(chdir(path_basename(tmpctx, argv[0])) == 0); + + /* FIXME: Support explicit path args? */ + init(&argc, &argv); + d = opendir("."); + while ((di = readdir(d)) != NULL) { + u8 *contents; + if (streq(di->d_name, ".") || streq(di->d_name, "..")) + continue; + contents = grab_file(tmpctx, di->d_name); + assert(contents); + run(contents, tal_bytelen(contents)-1); + } + closedir(d); + common_shutdown(); +} + +/* We never call any functions which might call these */ +size_t LLVMFuzzerMutate(uint8_t *data, size_t size, size_t max_size); +size_t LLVMFuzzerMutate(uint8_t *data, size_t size, size_t max_size) +{ + abort(); +} +#endif /* !FUZZING */ diff --git a/tests/fuzz/wire.h b/tests/fuzz/wire.h index 23d1ee1c1..fb2cfc8c6 100644 --- a/tests/fuzz/wire.h +++ b/tests/fuzz/wire.h @@ -21,7 +21,13 @@ static u8 *prefix_arr(const u8 *data, size_t size, u16 prefix) } /* The init function used by all fuzz-wire-* targets. */ -void init(int *argc, char ***argv) { common_setup("fuzzer"); dev_towire_allow_invalid_node_id = true; } +void init(int *argc, char ***argv) +{ + /* Don't call this if we're in unit-test mode, as libfuzz.c does it */ + if (!tmpctx) + common_setup("fuzzer"); + dev_towire_allow_invalid_node_id = true; +} /* Test that decoding arbitrary data does not crash. Then, if the data was * successfully decoded, test that encoding and decoding the message does not