diff --git a/CMakeLists.txt b/CMakeLists.txt index daf20f78c9..9507694c8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ set (NANO_ROCKSDB OFF CACHE BOOL "") set (NANO_POW_SERVER OFF CACHE BOOL "") set (NANO_WARN_TO_ERR OFF CACHE BOOL "") set (NANO_TIMED_LOCKS 0 CACHE STRING "") +set (NANO_FUZZER_TEST OFF CACHE BOOL "") option (NANO_STACKTRACE_BACKTRACE "Use BOOST_STACKTRACE_USE_BACKTRACE in stacktraces, for POSIX" OFF) if (NANO_STACKTRACE_BACKTRACE) @@ -110,6 +111,11 @@ else () add_definitions(-DED25519_NO_INLINE_ASM) endif() + if (NANO_FUZZER_TEST) + add_compile_options (-fsanitize=fuzzer-no-link -fno-omit-frame-pointer) + add_definitions (-DNANO_FUZZER_TEST) + endif () + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(i.86|x86(_64)?)$") if (NANO_SIMD_OPTIMIZATIONS OR RAIBLOCKS_SIMD_OPTIMIZATIONS OR ENABLE_AVX2) add_compile_options(-msse4) @@ -175,6 +181,9 @@ else () set (PLATFORM_LINK_FLAGS "${PLATFORM_LINK_FLAGS} -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/tsan_clang_blacklist") endif() endif() + if (NANO_FUZZER_TEST) + set (PLATFORM_LINK_FLAGS "${PLATFORM_LINK_FLAGS} -fsanitize=fuzzer-no-link") + endif () endif () SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINK_FLAGS}" ) @@ -352,6 +361,14 @@ add_subdirectory(nano/nano_node) add_subdirectory(nano/rpc) add_subdirectory(nano/nano_rpc) +if (NANO_FUZZER_TEST) + if (NOT WIN32) + add_subdirectory (nano/fuzzer_test) + else () + message (WARNING "Fuzzing is not supported on Windows") + endif () +endif () + if (NANO_TEST OR RAIBLOCKS_TEST) if(WIN32) if(MSVC_VERSION) diff --git a/docker/ci/Dockerfile-clang-6 b/docker/ci/Dockerfile-clang-6 new file mode 100644 index 0000000000..c292050cdb --- /dev/null +++ b/docker/ci/Dockerfile-clang-6 @@ -0,0 +1,22 @@ +FROM nanocurrency/nano-env:base + +RUN apt-get update && apt-get install -yqq software-properties-common && \ + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ + apt-add-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-6.0 main" && \ + apt-get update -qq && apt-get install -yqq \ + clang-6.0 lldb-6.0 libfuzzer-6.0-dev git + +ADD util/build_prep/fetch_rocksdb.sh fetch_rocksdb.sh +RUN ./fetch_rocksdb.sh + +ENV CXX=/usr/bin/clang++ +ENV CC=/usr/bin/clang +RUN ln -s /usr/bin/clang-6.0 /usr/bin/clang +RUN ln -s /usr/bin/clang++-6.0 /usr/bin/clang++ +RUN update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 +RUN update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 +ENV BOOST_ROOT=/tmp/boost + +ADD util/build_prep/bootstrap_boost.sh bootstrap_boost.sh + +RUN ./bootstrap_boost.sh -m -c -B 1.70 diff --git a/nano/fuzzer_test/CMakeLists.txt b/nano/fuzzer_test/CMakeLists.txt new file mode 100644 index 0000000000..0e02876e05 --- /dev/null +++ b/nano/fuzzer_test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(fuzz_buffer fuzz_buffer.cpp) +target_compile_options(fuzz_buffer PUBLIC -fsanitize=fuzzer) +target_link_libraries(fuzz_buffer PRIVATE -fsanitize=fuzzer node) diff --git a/nano/fuzzer_test/fuzz_buffer.cpp b/nano/fuzzer_test/fuzz_buffer.cpp new file mode 100644 index 0000000000..0c139e3e4e --- /dev/null +++ b/nano/fuzzer_test/fuzz_buffer.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace nano +{ +void force_nano_test_network (); +} +namespace +{ +std::shared_ptr system0; +std::shared_ptr node0; + +class fuzz_visitor : public nano::message_visitor +{ +public: + virtual void keepalive (nano::keepalive const &) override + { + } + virtual void publish (nano::publish const &) override + { + } + virtual void confirm_req (nano::confirm_req const &) override + { + } + virtual void confirm_ack (nano::confirm_ack const &) override + { + } + virtual void bulk_pull (nano::bulk_pull const &) override + { + } + virtual void bulk_pull_account (nano::bulk_pull_account const &) override + { + } + virtual void bulk_push (nano::bulk_push const &) override + { + } + virtual void frontier_req (nano::frontier_req const &) override + { + } + virtual void node_id_handshake (nano::node_id_handshake const &) override + { + } +}; +} + +/** Fuzz live message parsing. This covers parsing and block/vote uniquing. */ +void fuzz_message_parser (const uint8_t * Data, size_t Size) +{ + static bool initialized = false; + if (!initialized) + { + nano::force_nano_test_network (); + initialized = true; + system0 = std::make_shared (1); + node0 = system0->nodes[0]; + } + + fuzz_visitor visitor; + nano::message_parser parser (node0->block_uniquer, node0->vote_uniquer, visitor, node0->work); + parser.deserialize_buffer (Data, Size); +} + +/** Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput (const uint8_t * Data, size_t Size) +{ + fuzz_message_parser (Data, Size); + return 0; +} diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index fb07a09521..fe4ccbf6c4 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -1313,7 +1313,9 @@ std::shared_ptr nano::deserialize_block (nano::stream & stream_a, n break; } default: +#ifndef NANO_FUZZER_TEST assert (false); +#endif break; } if (uniquer_a != nullptr) diff --git a/nano/lib/work.cpp b/nano/lib/work.cpp index 700fe461ae..f7b55bd7fd 100644 --- a/nano/lib/work.cpp +++ b/nano/lib/work.cpp @@ -22,6 +22,7 @@ bool nano::work_validate (nano::block const & block_a, uint64_t * difficulty_a) return work_validate (block_a.root (), block_a.block_work (), difficulty_a); } +#ifndef NANO_FUZZER_TEST uint64_t nano::work_value (nano::root const & root_a, uint64_t work_a) { uint64_t result; @@ -32,6 +33,18 @@ uint64_t nano::work_value (nano::root const & root_a, uint64_t work_a) blake2b_final (&hash, reinterpret_cast (&result), sizeof (result)); return result; } +#else +uint64_t nano::work_value (nano::root const & root_a, uint64_t work_a) +{ + static nano::network_constants network_constants; + if (!network_constants.is_test_network ()) + { + assert (false); + std::exit (1); + } + return network_constants.publish_threshold + 1; +} +#endif nano::work_pool::work_pool (unsigned max_threads_a, std::chrono::nanoseconds pow_rate_limiter_a, std::function (nano::root const &, uint64_t, std::atomic &)> opencl_a) : ticket (0),