tests: add libFuzzer based fuzzing
authorPetr Štetiar <ynezz@true.cz>
Tue, 13 Oct 2020 12:36:44 +0000 (14:36 +0200)
committerPetr Štetiar <ynezz@true.cz>
Tue, 13 Oct 2020 13:27:49 +0000 (15:27 +0200)
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.

So lets use libFuzzer to fuzz dns_handle_packet for the start.

Ref: https://llvm.org/docs/LibFuzzer.html
Signed-off-by: Petr Štetiar <ynezz@true.cz>
CMakeLists.txt
tests/CMakeLists.txt [new file with mode: 0644]
tests/fuzz/CMakeLists.txt [new file with mode: 0644]
tests/fuzz/dict/mdns.dict [new file with mode: 0644]
tests/fuzz/inputs/query_qu.pcap [new file with mode: 0644]
tests/fuzz/test-fuzz.c [new file with mode: 0644]

index e08720327b7c608a5a42d05e72883bc9f40b738c..80d1cf5be3524079f166cbb4443bfc45610671fa 100644 (file)
@@ -28,6 +28,11 @@ TARGET_LINK_LIBRARIES(umdns-lib ${LIBS})
 ADD_EXECUTABLE(umdns main.c)
 TARGET_LINK_LIBRARIES(umdns umdns-lib)
 
+IF(UNIT_TESTING)
+  ENABLE_TESTING()
+  ADD_SUBDIRECTORY(tests)
+ENDIF()
+
 INSTALL(TARGETS umdns
        RUNTIME DESTINATION sbin
 )
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..02b121c
--- /dev/null
@@ -0,0 +1,3 @@
+IF(CMAKE_C_COMPILER_ID STREQUAL "Clang")
+  ADD_SUBDIRECTORY(fuzz)
+ENDIF()
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e2f9873
--- /dev/null
@@ -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} umdns-lib-san ${LIBS})
+  ADD_TEST(
+    NAME ${name}
+       COMMAND ${name} -max_len=256 -timeout=10 -max_total_time=300 ${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/dict/mdns.dict b/tests/fuzz/dict/mdns.dict
new file mode 100644 (file)
index 0000000..f8f80c1
--- /dev/null
@@ -0,0 +1,6 @@
+"\x0c"
+"\x78"
+"\xc0\xb0"
+"\x80\x01"
+"."
+"_"
diff --git a/tests/fuzz/inputs/query_qu.pcap b/tests/fuzz/inputs/query_qu.pcap
new file mode 100644 (file)
index 0000000..b1857a9
Binary files /dev/null and b/tests/fuzz/inputs/query_qu.pcap differ
diff --git a/tests/fuzz/test-fuzz.c b/tests/fuzz/test-fuzz.c
new file mode 100644 (file)
index 0000000..ca6caa1
--- /dev/null
@@ -0,0 +1,48 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "dns.h"
+#include "cache.c"
+#include "interface.h"
+
+int cfg_proto = 0;
+int cfg_no_subnet = 0;
+
+static void fuzz_dns_handle_packet(uint8_t *input, size_t size)
+{
+       struct sockaddr from;
+       struct interface iface;
+       struct cache_service *s, *t;
+
+       memset(&from, 0, sizeof(from));
+       memset(&iface, 0, sizeof(iface));
+
+       cache_init();
+       dns_handle_packet(&iface, &from, 1922, input, size);
+
+       avl_for_each_element_safe(&services, s, avl, t)
+               cache_service_free(s);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *input, size_t size)
+{
+       uint8_t *buf = calloc(1, size);
+       if (!buf)
+               return 0;
+
+       memcpy(buf, input, size);
+       fuzz_dns_handle_packet(buf, size);
+       free(buf);
+
+       return 0;
+}