diff --git a/.gitignore b/.gitignore index 1fb42bb7..d628511e 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ Makefile Makefile.in Testing/ install_manifest.txt +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ee246b6..4f8c6e20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ if (WIN32) endif() option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF) option(BUILD_TESTING "Build test programs" ON) +option(BUILD_FUZZING "Build with fuzzer" OFF) option(MAXMINDDB_BUILD_BINARIES "Build binaries" ON) option(MAXMINDDB_INSTALL "Generate the install target" ON) diff --git a/README.fuzzing.md b/README.fuzzing.md new file mode 100644 index 00000000..c0a4948d --- /dev/null +++ b/README.fuzzing.md @@ -0,0 +1,41 @@ +# Fuzzing libmaxminddb + +These tests are only meant to be run on GNU/Linux. + +## Build maxminddb fuzzer using libFuzzer. + +### Export flags for fuzzing. + +Note that in `CFLAGS` and `CXXFLAGS`, any type of sanitizers can be added. + +- [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html), + [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html), + [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html), + [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html), + [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html). + +```shell +$ export CC=clang +$ export CXX=clang++ +$ export CFLAGS="-g -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address,undefined -fsanitize=fuzzer-no-link" +$ export CXXFLAGS="-g -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address,undefined -fsanitize=fuzzer-no-link" +$ export LIB_FUZZING_ENGINE="-fsanitize=fuzzer" +``` + +### Build maxminddb for fuzzing. + +```shell +$ mkdir -p build && cd build +$ cmake -DBUILD_FUZZING=ON ../. +$ cmake --build . -j$(nproc) +``` + +### Running fuzzer. + +```shell +$ mkdir -p fuzz_mmdb_seed fuzz_mmdb_seed_corpus +$ find ../t/maxmind-db/test-data/ -type f -size -4k -exec cp {} ./fuzz_mmdb_seed_corpus/ \; +$ ./t/fuzz_mmdb fuzz_mmdb_seed/ fuzz_mmdb_seed_corpus/ +``` + +Here is more information about [LibFuzzer](https://llvm.org/docs/LibFuzzer.html). diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index 78679562..f0e91f76 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -52,3 +52,9 @@ foreach(TEST_TARGET_NAME ${TEST_TARGET_NAMES}) add_test( NAME ${TEST_TARGET_NAME} COMMAND ${TEST_TARGET_NAME} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/t) endforeach() + +if(BUILD_FUZZING) + add_executable(fuzz_mmdb fuzz_mmdb.c) + target_include_directories(fuzz_mmdb PRIVATE ../src) + target_link_libraries(fuzz_mmdb maxminddb $ENV{LIB_FUZZING_ENGINE}) +endif() diff --git a/t/fuzz_mmdb.c b/t/fuzz_mmdb.c new file mode 100644 index 00000000..97332681 --- /dev/null +++ b/t/fuzz_mmdb.c @@ -0,0 +1,36 @@ +#include "maxminddb-compat-util.h" +#include "maxminddb.h" +#include + +#define kMinInputLength 2 +#define kMaxInputLength 4048 + +extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + int status; + FILE *fp; + MMDB_s mmdb; + char filename[256]; + + if (size < kMinInputLength || size > kMaxInputLength) + return 0; + + sprintf(filename, "/tmp/libfuzzer.%d", getpid()); + + fp = fopen(filename, "wb"); + if (!fp) + return 0; + + fwrite(data, size, sizeof(uint8_t), fp); + fclose(fp); + + status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb); + if (status == MMDB_SUCCESS) + MMDB_close(&mmdb); + + unlink(filename); + return 0; +}