From 5f6bbd3eb944c9b5a90e4b99ff3baa3bda2b5ef8 Mon Sep 17 00:00:00 2001 From: Chad Fraleigh Date: Mon, 10 Jul 2023 18:35:34 -0700 Subject: [PATCH 1/4] Added undefined behavior sanitizer. Combined common sanitizer flags. Added fuzzers. --- build/CMakeLists.txt | 42 ++++- fuzzing/CMakeLists.txt | 54 ++++++ fuzzing/fuzz-Base32ToByteStream.cc | 32 ++++ fuzzing/fuzz-Base64ToByteStream.cc | 32 ++++ fuzzing/fuzz-BlindedPublicKey.cc | 23 +++ fuzzing/fuzz-ByteStreamToBase32.cc | 32 ++++ fuzzing/fuzz-ByteStreamToBase64.cc | 32 ++++ fuzzing/fuzz-HandleI2NPMessage.cc | 29 ++++ fuzzing/fuzz-IdentityEx.cc | 21 +++ fuzzing/fuzz-LeaseSet.cc | 21 +++ fuzzing/fuzz-LeaseSet2.cc | 38 ++++ fuzzing/fuzz-NetDb-AddRouterInfo.cc | 17 ++ fuzzing/fuzz-NetDb-HandleDatabaseLookupMsg.cc | 30 ++++ ...fuzz-NetDb-HandleDatabaseSearchReplyMsg.cc | 30 ++++ fuzzing/fuzz-NetDb-HandleDatabaseStoreMsg.cc | 30 ++++ .../fuzz-NetDb-HandleNTCP2RouterInfoMsg.cc | 30 ++++ fuzzing/fuzz-NetDb-PostI2NPMsg.cc | 30 ++++ ...-RouterContext-DecryptTunnelBuildRecord.cc | 24 +++ ...terContext-ProcessDeliveryStatusMessage.cc | 30 ++++ ...fuzz-RouterContext-ProcessGarlicMessage.cc | 30 ++++ fuzzing/fuzzing.h | 15 ++ fuzzing/fuzzing_impl.cc | 162 ++++++++++++++++++ fuzzing/fuzzing_throttle.cc | 33 ++++ 23 files changed, 813 insertions(+), 4 deletions(-) create mode 100644 fuzzing/CMakeLists.txt create mode 100644 fuzzing/fuzz-Base32ToByteStream.cc create mode 100644 fuzzing/fuzz-Base64ToByteStream.cc create mode 100644 fuzzing/fuzz-BlindedPublicKey.cc create mode 100644 fuzzing/fuzz-ByteStreamToBase32.cc create mode 100644 fuzzing/fuzz-ByteStreamToBase64.cc create mode 100644 fuzzing/fuzz-HandleI2NPMessage.cc create mode 100644 fuzzing/fuzz-IdentityEx.cc create mode 100644 fuzzing/fuzz-LeaseSet.cc create mode 100644 fuzzing/fuzz-LeaseSet2.cc create mode 100644 fuzzing/fuzz-NetDb-AddRouterInfo.cc create mode 100644 fuzzing/fuzz-NetDb-HandleDatabaseLookupMsg.cc create mode 100644 fuzzing/fuzz-NetDb-HandleDatabaseSearchReplyMsg.cc create mode 100644 fuzzing/fuzz-NetDb-HandleDatabaseStoreMsg.cc create mode 100644 fuzzing/fuzz-NetDb-HandleNTCP2RouterInfoMsg.cc create mode 100644 fuzzing/fuzz-NetDb-PostI2NPMsg.cc create mode 100644 fuzzing/fuzz-RouterContext-DecryptTunnelBuildRecord.cc create mode 100644 fuzzing/fuzz-RouterContext-ProcessDeliveryStatusMessage.cc create mode 100644 fuzzing/fuzz-RouterContext-ProcessGarlicMessage.cc create mode 100644 fuzzing/fuzzing.h create mode 100644 fuzzing/fuzzing_impl.cc create mode 100644 fuzzing/fuzzing_throttle.cc diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index f5d01a0b456..f68ba0aa5a4 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -38,6 +38,8 @@ option(WITH_UPNP "Include support for UPnP client" OFF) option(WITH_GIT_VERSION "Use git commit info as version" OFF) option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF) option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF) +option(WITH_UNDEFSANITIZER "Build with undefined sanitizer (unix only)" OFF) +option(BUILD_FUZZING "Build fuzzers (Clang only)" OFF) option(BUILD_TESTING "Build tests" OFF) IF(BUILD_TESTING) @@ -208,20 +210,46 @@ if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386") add_definitions(-D__AES__) endif() + +set(_SANITIZE_FLAGS "") + if(WITH_ADDRSANITIZER) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + list(APPEND _SANITIZE_FLAGS -fsanitize=address) endif() if(WITH_THREADSANITIZER) if(WITH_ADDRSANITIZER) message(FATAL_ERROR "thread sanitizer option cannot be combined with address sanitizer") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread") + list(APPEND _SANITIZE_FLAGS -fsanitize=thread) + endif() +endif() + +if(WITH_UNDEFSANITIZER) + list(APPEND _SANITIZE_FLAGS -fsanitize=undefined) + list(APPEND _SANITIZE_FLAGS -fno-sanitize=vptr) + list(APPEND _SANITIZE_FLAGS -fno-sanitize=enum) +endif() + +if(BUILD_FUZZING) + if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + list(APPEND _SANITIZE_FLAGS -fsanitize=fuzzer-no-link) + else() + message(FATAL_ERROR "Fuzzing not supported by your compiler") endif() endif() +if(NOT "${_SANITIZE_FLAGS}" STREQUAL "") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") + + list(JOIN _SANITIZE_FLAGS " " _X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_X}") + + # Is this really needed? Compiler (and CXX flags) used to link + #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_X}") +endif() + + # Use std::atomic instead of GCC builtins on macOS PowerPC: # For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111 # This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility. @@ -336,6 +364,8 @@ message(STATUS " GIT VERSION : ${WITH_GIT_VERSION}") endif() message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") +message(STATUS " UNDEFSANITIZER : ${WITH_UNDEFSANITIZER}") +message(STATUS " FUZZING : ${BUILD_FUZZING}") message(STATUS "---------------------------------------") if(WITH_BINARY) @@ -390,3 +420,7 @@ endif() if(BUILD_TESTING) add_subdirectory(${CMAKE_SOURCE_DIR}/tests ${CMAKE_CURRENT_BINARY_DIR}/tests) endif() + +if(BUILD_FUZZING) + add_subdirectory(${CMAKE_SOURCE_DIR}/fuzzing ${CMAKE_CURRENT_BINARY_DIR}/fuzzing) +endif() diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt new file mode 100644 index 00000000000..b18b4e9abb5 --- /dev/null +++ b/fuzzing/CMakeLists.txt @@ -0,0 +1,54 @@ + +include_directories( + ../libi2pd + ${Boost_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIR} +) + + +set(LIBS + libi2pd + ${Boost_LIBRARIES} + OpenSSL::SSL + OpenSSL::Crypto + ZLIB::ZLIB + Threads::Threads + ${CMAKE_REQUIRED_LIBRARIES} +) + +add_library(fuzzing OBJECT + fuzzing_impl.cc + fuzzing_throttle.cc + fuzzing.h) + +link_libraries(fuzzing) + +set(FUZZERS + Base32ToByteStream + Base64ToByteStream + BlindedPublicKey + ByteStreamToBase32 + ByteStreamToBase64 + HandleI2NPMessage + IdentityEx + LeaseSet + LeaseSet2 + NetDb-AddRouterInfo + NetDb-HandleDatabaseSearchReplyMsg + NetDb-HandleDatabaseStoreMsg + NetDb-HandleDatabaseLookupMsg + NetDb-HandleNTCP2RouterInfoMsg + NetDb-PostI2NPMsg + RouterContext-DecryptTunnelBuildRecord + RouterContext-ProcessDeliveryStatusMessage + RouterContext-ProcessGarlicMessage +) + +string(REPLACE "fuzzer-no-link" "fuzzer" _LINK_FLAGS "${_SANITIZE_FLAGS}") + +foreach(F IN LISTS FUZZERS) + add_executable(fuzz-${F} fuzz-${F}.cc) + target_link_libraries(fuzz-${F} ${LIBS}) + target_link_options(fuzz-${F} PRIVATE ${_LINK_FLAGS}) +endforeach() + diff --git a/fuzzing/fuzz-Base32ToByteStream.cc b/fuzzing/fuzz-Base32ToByteStream.cc new file mode 100644 index 00000000000..e42741f5519 --- /dev/null +++ b/fuzzing/fuzz-Base32ToByteStream.cc @@ -0,0 +1,32 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + size_t outlen; + uint8_t * out; + + + if(size < 2) + return true; + + outlen = (data[0] << 8) | data[1]; + outlen++; + + data += 2; + size -= 2; + + out = new uint8_t[outlen]; + i2p::data::Base32ToByteStream((const char *) data, size, out, outlen); + delete [] out; + + return true; +} diff --git a/fuzzing/fuzz-Base64ToByteStream.cc b/fuzzing/fuzz-Base64ToByteStream.cc new file mode 100644 index 00000000000..257bc159c89 --- /dev/null +++ b/fuzzing/fuzz-Base64ToByteStream.cc @@ -0,0 +1,32 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + size_t outlen; + uint8_t * out; + + + if(size < 2) + return true; + + outlen = (data[0] << 8) | data[1]; + outlen++; + + data += 2; + size -= 2; + + out = new uint8_t[outlen]; + i2p::data::Base64ToByteStream((const char *) data, size, out, outlen); + delete [] out; + + return true; +} diff --git a/fuzzing/fuzz-BlindedPublicKey.cc b/fuzzing/fuzz-BlindedPublicKey.cc new file mode 100644 index 00000000000..656b25160bd --- /dev/null +++ b/fuzzing/fuzz-BlindedPublicKey.cc @@ -0,0 +1,23 @@ + + +#include +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + std::string str((const char *) data, size); + i2p::data::BlindedPublicKey * bpk; + + + bpk = new i2p::data::BlindedPublicKey(str); + delete bpk; + + return true; +} diff --git a/fuzzing/fuzz-ByteStreamToBase32.cc b/fuzzing/fuzz-ByteStreamToBase32.cc new file mode 100644 index 00000000000..876cf14157c --- /dev/null +++ b/fuzzing/fuzz-ByteStreamToBase32.cc @@ -0,0 +1,32 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + size_t outlen; + char * out; + + + if(size < (2 + 1)) + return true; + + outlen = (data[0] << 8) | data[1]; + outlen++; + + data += 2; + size -= 2; + + out = new char[outlen]; + i2p::data::ByteStreamToBase32(data, size, out, outlen); + delete [] out; + + return true; +} diff --git a/fuzzing/fuzz-ByteStreamToBase64.cc b/fuzzing/fuzz-ByteStreamToBase64.cc new file mode 100644 index 00000000000..749dd35b86f --- /dev/null +++ b/fuzzing/fuzz-ByteStreamToBase64.cc @@ -0,0 +1,32 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + size_t outlen; + char * out; + + + if(size < (2 + 1)) + return true; + + outlen = (data[0] << 8) | data[1]; + outlen++; + + data += 2; + size -= 2; + + out = new char[outlen]; + i2p::data::ByteStreamToBase64(data, size, out, outlen); + delete [] out; + + return true; +} diff --git a/fuzzing/fuzz-HandleI2NPMessage.cc b/fuzzing/fuzz-HandleI2NPMessage.cc new file mode 100644 index 00000000000..fc7410be15d --- /dev/null +++ b/fuzzing/fuzz-HandleI2NPMessage.cc @@ -0,0 +1,29 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::HandleI2NPMessage( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzz-IdentityEx.cc b/fuzzing/fuzz-IdentityEx.cc new file mode 100644 index 00000000000..a909324c1ab --- /dev/null +++ b/fuzzing/fuzz-IdentityEx.cc @@ -0,0 +1,21 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::data::IdentityEx * ident; + + + ident = new i2p::data::IdentityEx(data, size); + delete ident; + + return true; +} diff --git a/fuzzing/fuzz-LeaseSet.cc b/fuzzing/fuzz-LeaseSet.cc new file mode 100644 index 00000000000..1620716dbc4 --- /dev/null +++ b/fuzzing/fuzz-LeaseSet.cc @@ -0,0 +1,21 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::data::LeaseSet * ls; + + + ls = new i2p::data::LeaseSet(data, size, false); + delete ls; + + return true; +} diff --git a/fuzzing/fuzz-LeaseSet2.cc b/fuzzing/fuzz-LeaseSet2.cc new file mode 100644 index 00000000000..5675eb8d2e7 --- /dev/null +++ b/fuzzing/fuzz-LeaseSet2.cc @@ -0,0 +1,38 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + uint8_t storeType; + i2p::data::LeaseSet2 * ls; + + + if(size < 1) + return true; + + storeType = data[0]; + + // Same check as in NetDb::HandleDatabaseStoreMsg() + if(storeType == i2p::data::NETDB_STORE_TYPE_LEASESET) + return true; + + data++; + size--; + + // Same check as in NetDb::HandleDatabaseStoreMsg() + if(size > i2p::data::MAX_LS_BUFFER_SIZE) + return true; + + ls = new i2p::data::LeaseSet2(storeType, data, size, false); + delete ls; + + return true; +} diff --git a/fuzzing/fuzz-NetDb-AddRouterInfo.cc b/fuzzing/fuzz-NetDb-AddRouterInfo.cc new file mode 100644 index 00000000000..fd698a4ce5c --- /dev/null +++ b/fuzzing/fuzz-NetDb-AddRouterInfo.cc @@ -0,0 +1,17 @@ + + +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::data::netdb.AddRouterInfo(data, size); + + return true; +} diff --git a/fuzzing/fuzz-NetDb-HandleDatabaseLookupMsg.cc b/fuzzing/fuzz-NetDb-HandleDatabaseLookupMsg.cc new file mode 100644 index 00000000000..17360df094b --- /dev/null +++ b/fuzzing/fuzz-NetDb-HandleDatabaseLookupMsg.cc @@ -0,0 +1,30 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::data::netdb.HandleDatabaseLookupMsg( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzz-NetDb-HandleDatabaseSearchReplyMsg.cc b/fuzzing/fuzz-NetDb-HandleDatabaseSearchReplyMsg.cc new file mode 100644 index 00000000000..574b6a87cd1 --- /dev/null +++ b/fuzzing/fuzz-NetDb-HandleDatabaseSearchReplyMsg.cc @@ -0,0 +1,30 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::data::netdb.HandleDatabaseSearchReplyMsg( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzz-NetDb-HandleDatabaseStoreMsg.cc b/fuzzing/fuzz-NetDb-HandleDatabaseStoreMsg.cc new file mode 100644 index 00000000000..3f0a225b272 --- /dev/null +++ b/fuzzing/fuzz-NetDb-HandleDatabaseStoreMsg.cc @@ -0,0 +1,30 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::data::netdb.HandleDatabaseStoreMsg( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzz-NetDb-HandleNTCP2RouterInfoMsg.cc b/fuzzing/fuzz-NetDb-HandleNTCP2RouterInfoMsg.cc new file mode 100644 index 00000000000..c9327dff225 --- /dev/null +++ b/fuzzing/fuzz-NetDb-HandleNTCP2RouterInfoMsg.cc @@ -0,0 +1,30 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::data::netdb.HandleNTCP2RouterInfoMsg( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzz-NetDb-PostI2NPMsg.cc b/fuzzing/fuzz-NetDb-PostI2NPMsg.cc new file mode 100644 index 00000000000..adffef40e09 --- /dev/null +++ b/fuzzing/fuzz-NetDb-PostI2NPMsg.cc @@ -0,0 +1,30 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::data::netdb.PostI2NPMsg( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzz-RouterContext-DecryptTunnelBuildRecord.cc b/fuzzing/fuzz-RouterContext-DecryptTunnelBuildRecord.cc new file mode 100644 index 00000000000..0921ed4002b --- /dev/null +++ b/fuzzing/fuzz-RouterContext-DecryptTunnelBuildRecord.cc @@ -0,0 +1,24 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + uint8_t clearText[i2p::ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + + + if(size < i2p::TUNNEL_BUILD_RECORD_SIZE) + return true; + + i2p::context.DecryptTunnelBuildRecord(data, clearText); + + return true; +} diff --git a/fuzzing/fuzz-RouterContext-ProcessDeliveryStatusMessage.cc b/fuzzing/fuzz-RouterContext-ProcessDeliveryStatusMessage.cc new file mode 100644 index 00000000000..e85e866d4ba --- /dev/null +++ b/fuzzing/fuzz-RouterContext-ProcessDeliveryStatusMessage.cc @@ -0,0 +1,30 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::context.ProcessDeliveryStatusMessage( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzz-RouterContext-ProcessGarlicMessage.cc b/fuzzing/fuzz-RouterContext-ProcessGarlicMessage.cc new file mode 100644 index 00000000000..004722caaf5 --- /dev/null +++ b/fuzzing/fuzz-RouterContext-ProcessGarlicMessage.cc @@ -0,0 +1,30 @@ + + +#include +#include + +#include +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + i2p::I2NPMessageType msgType; + + + if(size < 1) + return true; + + msgType = (i2p::I2NPMessageType) data[0]; + + data++; + size--; + + i2p::context.ProcessGarlicMessage( + i2p::CreateI2NPMessage(msgType, data, size)); + + return true; +} diff --git a/fuzzing/fuzzing.h b/fuzzing/fuzzing.h new file mode 100644 index 00000000000..9f1b3b4c2ee --- /dev/null +++ b/fuzzing/fuzzing.h @@ -0,0 +1,15 @@ +#ifndef _FUZZING_H_ +#define _FUZZING_H_ + +#include +#include + + +void fuzzing_tick(void); + +void fuzzing_throttle(void); + +bool fuzzing_testinput(const uint8_t * data, size_t size); + + +#endif /* !_FUZZING_H_ */ diff --git a/fuzzing/fuzzing_impl.cc b/fuzzing/fuzzing_impl.cc new file mode 100644 index 00000000000..c1066e77615 --- /dev/null +++ b/fuzzing/fuzzing_impl.cc @@ -0,0 +1,162 @@ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzzing.h" + + +static bool (*runner)(const uint8_t *, size_t); + + +static +bool +run_single(const uint8_t * data, size_t size) +{ + bool status; + + + status = fuzzing_testinput(data, size); + + fuzzing_tick(); + fuzzing_throttle(); + + return status; +} + + +static +bool +run_batch(const uint8_t * data, size_t size) +{ + bool status; + size_t chunklen; + + + if(size < 2) + { + // XXX - Test something to prevent fuzzer from giving up + status = fuzzing_testinput(data, size); + fuzzing_tick(); + } + else + { + status = false; + + while(size >= 2) + { + chunklen = (data[0] << 8) | data[1]; + + data += 2; + size -= 2; + + if(chunklen > size) + chunklen = size; + + if(fuzzing_testinput(data, chunklen)) + status = true; + + data += chunklen; + size -= chunklen; + + fuzzing_tick(); + } + } + + fuzzing_throttle(); + + return status; +} + + +static +void +do_stop(void) +{ + i2p::tunnel::tunnels.Stop(); + i2p::transport::transports.Stop(); + i2p::data::netdb.Stop(); + i2p::log::Logger().Stop(); +} + + +static +void +do_setup(void) +{ + i2p::log::Logger().Start(); + i2p::log::Logger().SetLogLevel("critical"); + + i2p::config::Init(); + i2p::config::ParseCmdline(1, (char *[]) { (char *) "foo" }); + + // Disable networking + i2p::config::SetOption("ipv4", false); + i2p::config::SetOption("ipv6", false); + + i2p::fs::DetectDataDir("testdata", false); + i2p::fs::Init(); + + i2p::context.SetNetID(I2PD_NET_ID); + i2p::context.Init(); + + i2p::data::netdb.Start(); + + i2p::transport::transports.Start(true, true); + + i2p::tunnel::tunnels.Start(); + + // Stop threads before destructor called to avoid crash on exit + atexit(do_stop); +} + + +static +bool +do_init(void) +{ + do_setup(); + + // + // If FUZZING_BATCH env variable set, run batch mode. + // + // Pros: + // More data queued at once before time to process/empty all of it + // Better change of hitting thread bugs + // + // Cons: + // Input test data limited to 64k + // Input buffer under/over-reads may go un-noticed + // + if(getenv("FUZZING_BATCH") != nullptr) + runner = run_batch; + else + runner = run_single; + + return true; +} + + +extern "C" +int +LLVMFuzzerTestOneInput(const uint8_t * data, size_t size) +{ + static bool inited = do_init(); + + + // Suppress compiler warning + (void) inited; + + return runner(data, size) ? 0 : -1; +} diff --git a/fuzzing/fuzzing_throttle.cc b/fuzzing/fuzzing_throttle.cc new file mode 100644 index 00000000000..5591fa3ee6c --- /dev/null +++ b/fuzzing/fuzzing_throttle.cc @@ -0,0 +1,33 @@ + +#include + +#include "fuzzing.h" + + +static unsigned int counter = 0; + + +void +fuzzing_tick(void) +{ + counter++; +} + + +void +fuzzing_throttle(void) +{ + unsigned int delay; + + + // Give queues time to drain (avoid OOM or crash) + // - Too high a delay slows down fuzzing + // - Too low a delay causes intermittent crash on exit + delay = 50 + (counter / 50); + counter = 0; + + if(delay > 5000) + delay = 5000; + + std::this_thread::sleep_for (std::chrono::milliseconds(delay)); +} From acfa3938ba87cea85c67b8008176d83de7317e90 Mon Sep 17 00:00:00 2001 From: Chad Fraleigh Date: Tue, 15 Aug 2023 19:16:38 -0700 Subject: [PATCH 2/4] Added ToBase64Standard fuzzer. --- fuzzing/CMakeLists.txt | 1 + fuzzing/fuzz-ToBase64Standard.cc | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 fuzzing/fuzz-ToBase64Standard.cc diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index b18b4e9abb5..5f079d3b5f1 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -42,6 +42,7 @@ set(FUZZERS RouterContext-DecryptTunnelBuildRecord RouterContext-ProcessDeliveryStatusMessage RouterContext-ProcessGarlicMessage + ToBase64Standard ) string(REPLACE "fuzzer-no-link" "fuzzer" _LINK_FLAGS "${_SANITIZE_FLAGS}") diff --git a/fuzzing/fuzz-ToBase64Standard.cc b/fuzzing/fuzz-ToBase64Standard.cc new file mode 100644 index 00000000000..2707d74d821 --- /dev/null +++ b/fuzzing/fuzz-ToBase64Standard.cc @@ -0,0 +1,21 @@ + + +#include +#include +#include + +#include + +#include "fuzzing.h" + + +bool +fuzzing_testinput(const uint8_t * data, size_t size) +{ + std::string str((const char *) data, size); + + + i2p::data::ToBase64Standard(str); + + return true; +} From 7c9c5ac12eee5bea6462c52dba0c1cf6c38f2c82 Mon Sep 17 00:00:00 2001 From: Chad Fraleigh Date: Tue, 15 Aug 2023 19:35:48 -0700 Subject: [PATCH 3/4] Added VERIFY_ALWAYS_SUCCEEDS conditional to allow signature verifiers to always succeed during fuzzing for maximum code coverage. --- libi2pd/Ed25519.cpp | 5 +++++ libi2pd/Gost.cpp | 5 +++++ libi2pd/Signature.cpp | 8 +++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libi2pd/Ed25519.cpp b/libi2pd/Ed25519.cpp index 3e0795d57cc..e0fe38ee944 100644 --- a/libi2pd/Ed25519.cpp +++ b/libi2pd/Ed25519.cpp @@ -126,6 +126,11 @@ namespace crypto BN_CTX_free (ctx); if (!passed) LogPrint (eLogError, "25519 signature verification failed"); + +#ifdef VERIFY_ALWAYS_SUCCEEDS + passed = true; +#endif + return passed; } diff --git a/libi2pd/Gost.cpp b/libi2pd/Gost.cpp index 2dafc9ae5d3..b81fc74b85c 100644 --- a/libi2pd/Gost.cpp +++ b/libi2pd/Gost.cpp @@ -102,6 +102,11 @@ namespace crypto EC_POINT_free (C); BN_CTX_end (ctx); BN_CTX_free (ctx); + +#ifdef VERIFY_ALWAYS_SUCCEEDS + ret = true; +#endif + return ret; } diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index ebc188a928e..c8378dc8cc6 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -34,7 +34,13 @@ namespace crypto bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const { - return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len); + bool ret = EVP_DigestVerify (m_MDCtx, signature, 64, buf, len); + +#ifdef VERIFY_ALWAYS_SUCCEEDS + ret = true; +#endif + + return ret; } #else From fc7f7249fad01f2c6dd9c3317dfa08bf19d78663 Mon Sep 17 00:00:00 2001 From: Chad Fraleigh Date: Wed, 16 Aug 2023 13:47:15 -0700 Subject: [PATCH 4/4] Added VERIFY_ALWAYS_SUCCEEDS conflict/safety checking. --- build/CMakeLists.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index f68ba0aa5a4..d00eabe482e 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -249,6 +249,27 @@ if(NOT "${_SANITIZE_FLAGS}" STREQUAL "") #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_X}") endif() +# Check for incompatible VERIFY_ALWAYS_SUCCEEDS combinations +if(VERIFY_ALWAYS_SUCCEEDS) + if(NOT BUILD_FUZZING) + message(FATAL_ERROR "VERIFY_ALWAYS_SUCCEEDS enabled without BUILD_FUZZING") + endif() + + if(BUILD_TESTING) + # Tests may be invalidated + message(FATAL_ERROR "VERIFY_ALWAYS_SUCCEEDS enabled with BUILD_TESTING") + endif() + + if(WITH_LIBRARY) + # Such libraries are unsafe, except for fuzzing + message(FATAL_ERROR "VERIFY_ALWAYS_SUCCEEDS enabled with WITH_LIBRARY") + endif() + + if(WITH_BINARY) + # Such binaries are unsafe + message(FATAL_ERROR "VERIFY_ALWAYS_SUCCEEDS enabled with WITH_BINARY") + endif() +endif() # Use std::atomic instead of GCC builtins on macOS PowerPC: # For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111