From a615818198005732f587c09d43cc7f0540a52043 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Petr=20=C5=A0tetiar?= Date: Sun, 11 Oct 2020 12:46:43 +0200 Subject: [PATCH] Add fuzzing of multipart_parser MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit LibFuzzer is in-process, coverage-guided, evolutionary fuzzing engine. LibFuzzer is linked with the library under test, and feeds fuzzed inputs to the library via a specific fuzzing entrypoint (aka "target function"); the fuzzer then tracks which areas of the code are reached, and generates mutations on the corpus of input data in order to maximize the code coverage. Lets use libFuzzer to fuzz multipart_parser for the start. Ref: https://llvm.org/docs/LibFuzzer.html Signed-off-by: Petr Å tetiar --- CMakeLists.txt | 4 ++ tests/CMakeLists.txt | 3 ++ tests/fuzz-multipart-parser/CMakeLists.txt | 18 ++++++++ tests/fuzz-multipart-parser/corpus/.keep | 0 tests/fuzz-multipart-parser/dict/parser.dict | 10 +++++ tests/fuzz-multipart-parser/inputs/input1.txt | 6 +++ tests/fuzz-multipart-parser/inputs/input2.txt | 10 +++++ .../test-fuzz-multipart-parser.c | 43 +++++++++++++++++++ 8 files changed, 94 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/fuzz-multipart-parser/CMakeLists.txt create mode 100644 tests/fuzz-multipart-parser/corpus/.keep create mode 100644 tests/fuzz-multipart-parser/dict/parser.dict create mode 100644 tests/fuzz-multipart-parser/inputs/input1.txt create mode 100644 tests/fuzz-multipart-parser/inputs/input2.txt create mode 100644 tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 693830a..b60d08e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,5 +24,9 @@ ADD_LIBRARY(cgi-lib STATIC multipart_parser.c util.c) ADD_EXECUTABLE(cgi-io main.c) TARGET_LINK_LIBRARIES(cgi-io cgi-lib ${ubox} ${ubus}) +IF(UNIT_TESTING) + ENABLE_TESTING() + ADD_SUBDIRECTORY(tests) +ENDIF() INSTALL(TARGETS cgi-io RUNTIME DESTINATION sbin) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..8bb2a59 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") + ADD_SUBDIRECTORY(fuzz-multipart-parser) +ENDIF() diff --git a/tests/fuzz-multipart-parser/CMakeLists.txt b/tests/fuzz-multipart-parser/CMakeLists.txt new file mode 100644 index 0000000..0a3a989 --- /dev/null +++ b/tests/fuzz-multipart-parser/CMakeLists.txt @@ -0,0 +1,18 @@ +FILE(GLOB test_cases "test-*.c") + +MACRO(ADD_FUZZER_TEST name) + ADD_EXECUTABLE(${name} ${name}.c) + TARGET_COMPILE_OPTIONS(${name} PRIVATE -g -O1 -fno-omit-frame-pointer -fsanitize=fuzzer,address,leak,undefined) + TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR}) + TARGET_LINK_OPTIONS(${name} PRIVATE -stdlib=libc++ -fsanitize=fuzzer,address,leak,undefined) + TARGET_LINK_LIBRARIES(${name} cgi-lib) + ADD_TEST( + NAME ${name} + COMMAND ${name} -max_len=256 -timeout=10 -max_total_time=300 -dict=${CMAKE_CURRENT_SOURCE_DIR}/dict/parser.dict ${CMAKE_CURRENT_SOURCE_DIR}/corpus + ) +ENDMACRO(ADD_FUZZER_TEST) + +FOREACH(test_case ${test_cases}) + GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE) + ADD_FUZZER_TEST(${test_case}) +ENDFOREACH(test_case) diff --git a/tests/fuzz-multipart-parser/corpus/.keep b/tests/fuzz-multipart-parser/corpus/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/fuzz-multipart-parser/dict/parser.dict b/tests/fuzz-multipart-parser/dict/parser.dict new file mode 100644 index 0000000..72a5bdd --- /dev/null +++ b/tests/fuzz-multipart-parser/dict/parser.dict @@ -0,0 +1,10 @@ +"Content-Disposition: form-data; name=" +"\x0D\x0A" +"\x0D" +"x0A" +"=" +";" +"--" +"Content-Type:" +"filename=" +"--12--34--56" diff --git a/tests/fuzz-multipart-parser/inputs/input1.txt b/tests/fuzz-multipart-parser/inputs/input1.txt new file mode 100644 index 0000000..849873e --- /dev/null +++ b/tests/fuzz-multipart-parser/inputs/input1.txt @@ -0,0 +1,6 @@ +--AaB03x +Content-Disposition: form-data; name="files"; filename="fi;le1.txt" +Content-Type: text/plain + +contents +--AaB03x-- \ No newline at end of file diff --git a/tests/fuzz-multipart-parser/inputs/input2.txt b/tests/fuzz-multipart-parser/inputs/input2.txt new file mode 100644 index 0000000..7ba0099 --- /dev/null +++ b/tests/fuzz-multipart-parser/inputs/input2.txt @@ -0,0 +1,10 @@ +--AaB03x +Content-Disposition: form-data; name="submit-name" + +Larry +--AaB03x +Content-Disposition: form-data; name="files"; filename="file1.txt" +Content-Type: text/plain + + +--AaB03x-- diff --git a/tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c b/tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c new file mode 100644 index 0000000..ca952d9 --- /dev/null +++ b/tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c @@ -0,0 +1,43 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "multipart_parser.h" + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t size) +{ + char *buf = NULL; + multipart_parser *p; + static multipart_parser_settings s = { + .on_part_data = NULL, + .on_headers_complete = NULL, + .on_part_data_end = NULL, + .on_header_field = NULL, + .on_header_value = NULL, + }; + buf = calloc(1, size + 1); + if (!buf) + return 0; + + memcpy(buf, input, size); + p = multipart_parser_init(buf, &s); + if (!p) { + free(buf); + return 0; + } + + multipart_parser_execute(p, buf, size + 1); + multipart_parser_free(p); + free(buf); + + return 0; +} -- 2.30.2