From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp3.mail.ru (smtp3.mail.ru [94.100.179.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 7A54645C304 for ; Mon, 30 Nov 2020 23:25:20 +0300 (MSK) From: sergeyb@tarantool.org Date: Mon, 30 Nov 2020 23:24:46 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH 1/4] test: add infrastructure for fuzzing testing and fuzzers List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: tarantool-patches@dev.tarantool.org, imun@tarantool.org, sergepetrenko@tarantool.org From: Sergey Bronnikov There is a number of bugs related to parsing and encoding/decoding data. Examples: - csv: #2692, #4497, #2692 - uri: #585 One of the effective method to find such issues is a fuzzing testing. Patch introduce a CMake flag to enable building fuzzers (ENABLE_FUZZER) and add fuzzers based on LibFuzzer [1] to csv, http_parser and uri modules. NOTE: LibFuzzer requires Clang compiler. [1] https://llvm.org/docs/LibFuzzer.html How-To Use: $ mkdir build && cd build $ CC=clang CXX=clang++ cmake -DENABLE_FUZZER=ON -DENABLE_ASAN=ON -DCMAKE_BUILD_TYPE=Debug .. $ make fuzzers $ ./test/fuzz/csv_fuzzer -max_total_time=60*60*60 -workers=4 ../test/static/corpus/csv Part of #1809 --- CMakeLists.txt | 2 +- cmake/profile.cmake | 13 ++++++++++ test/CMakeLists.txt | 3 +++ test/fuzz/CMakeLists.txt | 45 ++++++++++++++++++++++++++++++++++ test/fuzz/csv_fuzzer.c | 23 +++++++++++++++++ test/fuzz/http_parser_fuzzer.c | 18 ++++++++++++++ test/fuzz/uri_fuzzer.c | 19 ++++++++++++++ 7 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 test/fuzz/CMakeLists.txt create mode 100644 test/fuzz/csv_fuzzer.c create mode 100644 test/fuzz/http_parser_fuzzer.c create mode 100644 test/fuzz/uri_fuzzer.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fa6818f8e..8bc6f22bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -607,7 +607,7 @@ set(PREFIX ${CMAKE_INSTALL_PREFIX}) set(options PACKAGE VERSION BUILD C_COMPILER CXX_COMPILER C_FLAGS CXX_FLAGS PREFIX ENABLE_SSE2 ENABLE_AVX - ENABLE_GCOV ENABLE_GPROF ENABLE_VALGRIND ENABLE_ASAN ENABLE_UB_SANITIZER + ENABLE_GCOV ENABLE_GPROF ENABLE_VALGRIND ENABLE_ASAN ENABLE_UB_SANITIZER ENABLE_FUZZER ENABLE_BACKTRACE ENABLE_DOC ENABLE_DIST diff --git a/cmake/profile.cmake b/cmake/profile.cmake index bc4bf67f5..45e3d112c 100644 --- a/cmake/profile.cmake +++ b/cmake/profile.cmake @@ -42,6 +42,19 @@ else() add_definitions(-DNVALGRIND=1) endif() +option(OSS_FUZZ "Set this option to use flags by oss-fuzz" OFF) +option(ENABLE_FUZZER "Enable fuzzing testing" OFF) +if(ENABLE_FUZZER) + if(CMAKE_COMPILER_IS_GNUCC) + message(FATAL_ERROR + "\n" + "Fuzzing is unsupported with GCC compiler. Use Clang:\n" + " $ git clean -xfd; git submodule foreach --recursive git clean -xfd\n" + " $ CC=clang CXX=clang++ cmake . <...> -DENABLE_FUZZER=ON && make -j\n" + "\n") + endif() +endif() + option(ENABLE_ASAN "Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF) if (ENABLE_ASAN) if (CMAKE_COMPILER_IS_GNUCC) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 10882c6a1..d20a4eb5d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -75,6 +75,9 @@ add_subdirectory(app-tap) add_subdirectory(box) add_subdirectory(box-tap) add_subdirectory(unit) +if(ENABLE_FUZZER) + add_subdirectory(fuzz) +endif() add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/luajit/test ${PROJECT_BINARY_DIR}/third_party/luajit/test) diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt new file mode 100644 index 000000000..142d38f67 --- /dev/null +++ b/test/fuzz/CMakeLists.txt @@ -0,0 +1,45 @@ +include_directories(${PROJECT_SOURCE_DIR}/src) +include_directories(${PROJECT_BINARY_DIR}/src) +include_directories(${PROJECT_SOURCE_DIR}/src/box) + +# A special target with fuzzer and sanitizer flags. +add_library(fuzzer_config INTERFACE) + +target_compile_options( + fuzzer_config + INTERFACE + $<$: + -fsanitize=fuzzer,address + > + $<$: + -fsanitize=fuzzer,undefined + > +) +target_link_libraries( + fuzzer_config + INTERFACE + $<$: + -fsanitize=fuzzer,address + > + $<$: + -fsanitize=fuzzer,undefined + > +) + +# Use PUBLIC to force 'fuzzer_config' for all dependent targets. +add_executable(csv_fuzzer csv_fuzzer.c) +target_link_libraries(csv_fuzzer PUBLIC csv fuzzer_config) + +add_executable(uri_fuzzer uri_fuzzer.c) +target_link_libraries(uri_fuzzer PUBLIC uri fuzzer_config) + +add_executable(http_parser_fuzzer http_parser_fuzzer.c) +target_link_libraries(http_parser_fuzzer PUBLIC http_parser fuzzer_config) + +set(fuzzing_binaries csv_fuzzer + http_parser_fuzzer + uri_fuzzer) + +add_custom_target(fuzzers + DEPENDS ${fuzzing_binaries} + COMMENT "Build fuzzers") diff --git a/test/fuzz/csv_fuzzer.c b/test/fuzz/csv_fuzzer.c new file mode 100644 index 000000000..8853d6308 --- /dev/null +++ b/test/fuzz/csv_fuzzer.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include "csv/csv.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct csv csv; + csv_create(&csv); + char *buf = calloc(size, sizeof(char*)); + if (buf == NULL) + return -1; + memcpy(buf, data, size); + buf[size] = '\0'; + char *end = buf + size; + csv_parse_chunk(&csv, buf, end); + csv_finish_parsing(&csv); + int rc = csv_get_error_status(&csv) == CSV_ER_INVALID ? 1 : 0; + csv_destroy(&csv); + free(buf); + + return rc; +} diff --git a/test/fuzz/http_parser_fuzzer.c b/test/fuzz/http_parser_fuzzer.c new file mode 100644 index 000000000..a0aaf6786 --- /dev/null +++ b/test/fuzz/http_parser_fuzzer.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include "http_parser/http_parser.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct http_parser parser; + char *buf = (char*)data; + http_parser_create(&parser); + parser.hdr_name = (char *)calloc((int)size, sizeof(char)); + if (parser.hdr_name == NULL) + return -1; + char *end_buf = buf + size; + int rc = http_parse_header_line(&parser, &buf, end_buf, size); + free(parser.hdr_name); + + return rc; +} diff --git a/test/fuzz/uri_fuzzer.c b/test/fuzz/uri_fuzzer.c new file mode 100644 index 000000000..8397505bd --- /dev/null +++ b/test/fuzz/uri_fuzzer.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include "uri/uri.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *buf = calloc(size, sizeof(char*)); + if (!buf) + return -1; + strncpy(buf, (char*)data, size); + buf[size] = '\0'; + struct uri uri; + int rc = uri_parse(&uri, buf); + free(buf); + + return rc; +} -- 2.25.1