Browse Source

Added undefined behavior sanitizer.

Combined common sanitizer flags.
Added fuzzers.
pull/1949/head
Chad Fraleigh 1 year ago
parent
commit
5f6bbd3eb9
No known key found for this signature in database
GPG Key ID: 2415C39758458A8F
  1. 42
      build/CMakeLists.txt
  2. 54
      fuzzing/CMakeLists.txt
  3. 32
      fuzzing/fuzz-Base32ToByteStream.cc
  4. 32
      fuzzing/fuzz-Base64ToByteStream.cc
  5. 23
      fuzzing/fuzz-BlindedPublicKey.cc
  6. 32
      fuzzing/fuzz-ByteStreamToBase32.cc
  7. 32
      fuzzing/fuzz-ByteStreamToBase64.cc
  8. 29
      fuzzing/fuzz-HandleI2NPMessage.cc
  9. 21
      fuzzing/fuzz-IdentityEx.cc
  10. 21
      fuzzing/fuzz-LeaseSet.cc
  11. 38
      fuzzing/fuzz-LeaseSet2.cc
  12. 17
      fuzzing/fuzz-NetDb-AddRouterInfo.cc
  13. 30
      fuzzing/fuzz-NetDb-HandleDatabaseLookupMsg.cc
  14. 30
      fuzzing/fuzz-NetDb-HandleDatabaseSearchReplyMsg.cc
  15. 30
      fuzzing/fuzz-NetDb-HandleDatabaseStoreMsg.cc
  16. 30
      fuzzing/fuzz-NetDb-HandleNTCP2RouterInfoMsg.cc
  17. 30
      fuzzing/fuzz-NetDb-PostI2NPMsg.cc
  18. 24
      fuzzing/fuzz-RouterContext-DecryptTunnelBuildRecord.cc
  19. 30
      fuzzing/fuzz-RouterContext-ProcessDeliveryStatusMessage.cc
  20. 30
      fuzzing/fuzz-RouterContext-ProcessGarlicMessage.cc
  21. 15
      fuzzing/fuzzing.h
  22. 162
      fuzzing/fuzzing_impl.cc
  23. 33
      fuzzing/fuzzing_throttle.cc

42
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_GIT_VERSION "Use git commit info as version" OFF)
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF) option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
option(WITH_THREADSANITIZER "Build with thread 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) option(BUILD_TESTING "Build tests" OFF)
IF(BUILD_TESTING) IF(BUILD_TESTING)
@ -208,20 +210,46 @@ if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386")
add_definitions(-D__AES__) add_definitions(-D__AES__)
endif() endif()
set(_SANITIZE_FLAGS "")
if(WITH_ADDRSANITIZER) if(WITH_ADDRSANITIZER)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") list(APPEND _SANITIZE_FLAGS -fsanitize=address)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
endif() endif()
if(WITH_THREADSANITIZER) if(WITH_THREADSANITIZER)
if(WITH_ADDRSANITIZER) if(WITH_ADDRSANITIZER)
message(FATAL_ERROR "thread sanitizer option cannot be combined with address sanitizer") message(FATAL_ERROR "thread sanitizer option cannot be combined with address sanitizer")
else() else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") list(APPEND _SANITIZE_FLAGS -fsanitize=thread)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_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()
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: # Use std::atomic instead of GCC builtins on macOS PowerPC:
# For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111 # 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. # 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() endif()
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
message(STATUS " UNDEFSANITIZER : ${WITH_UNDEFSANITIZER}")
message(STATUS " FUZZING : ${BUILD_FUZZING}")
message(STATUS "---------------------------------------") message(STATUS "---------------------------------------")
if(WITH_BINARY) if(WITH_BINARY)
@ -390,3 +420,7 @@ endif()
if(BUILD_TESTING) if(BUILD_TESTING)
add_subdirectory(${CMAKE_SOURCE_DIR}/tests ${CMAKE_CURRENT_BINARY_DIR}/tests) add_subdirectory(${CMAKE_SOURCE_DIR}/tests ${CMAKE_CURRENT_BINARY_DIR}/tests)
endif() endif()
if(BUILD_FUZZING)
add_subdirectory(${CMAKE_SOURCE_DIR}/fuzzing ${CMAKE_CURRENT_BINARY_DIR}/fuzzing)
endif()

54
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()

32
fuzzing/fuzz-Base32ToByteStream.cc

@ -0,0 +1,32 @@
#include <stdint.h>
#include <stddef.h>
#include <Base.h>
#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;
}

32
fuzzing/fuzz-Base64ToByteStream.cc

@ -0,0 +1,32 @@
#include <stdint.h>
#include <stddef.h>
#include <Base.h>
#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;
}

23
fuzzing/fuzz-BlindedPublicKey.cc

@ -0,0 +1,23 @@
#include <stdint.h>
#include <stddef.h>
#include <string>
#include <Blinding.h>
#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;
}

32
fuzzing/fuzz-ByteStreamToBase32.cc

@ -0,0 +1,32 @@
#include <stdint.h>
#include <stddef.h>
#include <Base.h>
#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;
}

32
fuzzing/fuzz-ByteStreamToBase64.cc

@ -0,0 +1,32 @@
#include <stdint.h>
#include <stddef.h>
#include <Base.h>
#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;
}

29
fuzzing/fuzz-HandleI2NPMessage.cc

@ -0,0 +1,29 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#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;
}

21
fuzzing/fuzz-IdentityEx.cc

@ -0,0 +1,21 @@
#include <stdint.h>
#include <stddef.h>
#include <Identity.h>
#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;
}

21
fuzzing/fuzz-LeaseSet.cc

@ -0,0 +1,21 @@
#include <stdint.h>
#include <stddef.h>
#include <LeaseSet.h>
#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;
}

38
fuzzing/fuzz-LeaseSet2.cc

@ -0,0 +1,38 @@
#include <stdint.h>
#include <stddef.h>
#include <LeaseSet.h>
#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;
}

17
fuzzing/fuzz-NetDb-AddRouterInfo.cc

@ -0,0 +1,17 @@
#include <stdint.h>
#include <stddef.h>
#include <NetDb.hpp>
#include "fuzzing.h"
bool
fuzzing_testinput(const uint8_t * data, size_t size)
{
i2p::data::netdb.AddRouterInfo(data, size);
return true;
}

30
fuzzing/fuzz-NetDb-HandleDatabaseLookupMsg.cc

@ -0,0 +1,30 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <NetDb.hpp>
#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;
}

30
fuzzing/fuzz-NetDb-HandleDatabaseSearchReplyMsg.cc

@ -0,0 +1,30 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <NetDb.hpp>
#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;
}

30
fuzzing/fuzz-NetDb-HandleDatabaseStoreMsg.cc

@ -0,0 +1,30 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <NetDb.hpp>
#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;
}

30
fuzzing/fuzz-NetDb-HandleNTCP2RouterInfoMsg.cc

@ -0,0 +1,30 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <NetDb.hpp>
#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;
}

30
fuzzing/fuzz-NetDb-PostI2NPMsg.cc

@ -0,0 +1,30 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <NetDb.hpp>
#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;
}

24
fuzzing/fuzz-RouterContext-DecryptTunnelBuildRecord.cc

@ -0,0 +1,24 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <RouterContext.h>
#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;
}

30
fuzzing/fuzz-RouterContext-ProcessDeliveryStatusMessage.cc

@ -0,0 +1,30 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <RouterContext.h>
#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;
}

30
fuzzing/fuzz-RouterContext-ProcessGarlicMessage.cc

@ -0,0 +1,30 @@
#include <stdint.h>
#include <stddef.h>
#include <I2NPProtocol.h>
#include <RouterContext.h>
#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;
}

15
fuzzing/fuzzing.h

@ -0,0 +1,15 @@
#ifndef _FUZZING_H_
#define _FUZZING_H_
#include <stdint.h>
#include <stddef.h>
void fuzzing_tick(void);
void fuzzing_throttle(void);
bool fuzzing_testinput(const uint8_t * data, size_t size);
#endif /* !_FUZZING_H_ */

162
fuzzing/fuzzing_impl.cc

@ -0,0 +1,162 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <Config.h>
#include <Log.h>
#include <FS.h>
#include <Destination.h>
#include <NetDb.hpp>
#include <Tunnel.h>
#include <RouterContext.h>
#include <Transports.h>
#include <version.h>
#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;
}

33
fuzzing/fuzzing_throttle.cc

@ -0,0 +1,33 @@
#include <thread>
#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));
}
Loading…
Cancel
Save