Add fuzzing of utility functions
authorPetr Štetiar <ynezz@true.cz>
Sun, 11 Oct 2020 11:21:16 +0000 (13:21 +0200)
committerPetr Štetiar <ynezz@true.cz>
Tue, 27 Oct 2020 22:03:04 +0000 (23:03 +0100)
Thus increase fuzzing coverage.

Signed-off-by: Petr Štetiar <ynezz@true.cz>
tests/CMakeLists.txt
tests/fuzz/CMakeLists.txt [new file with mode: 0644]
tests/fuzz/corpus/58668e7669fd564d99db5d581fcdb6a5618440b5 [new file with mode: 0644]
tests/fuzz/corpus/5ba93c9db0cff93f52b521d7420e43f6eda2784f [new file with mode: 0644]
tests/fuzz/corpus/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc [new file with mode: 0644]
tests/fuzz/test-fuzz.c [new file with mode: 0644]
util.c
util.h

index 8bb2a59412da3500b7e570a47a2eeafafe469e7e..efad20642dd6a5e6db307cccc4068f3a4bfe92d5 100644 (file)
@@ -1,3 +1,4 @@
 IF(CMAKE_C_COMPILER_ID STREQUAL "Clang")
+  ADD_SUBDIRECTORY(fuzz)
   ADD_SUBDIRECTORY(fuzz-multipart-parser)
 ENDIF()
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b4df45c
--- /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} cgi-lib)
+  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/corpus/58668e7669fd564d99db5d581fcdb6a5618440b5 b/tests/fuzz/corpus/58668e7669fd564d99db5d581fcdb6a5618440b5
new file mode 100644 (file)
index 0000000..22aac29
--- /dev/null
@@ -0,0 +1 @@
+J
\ No newline at end of file
diff --git a/tests/fuzz/corpus/5ba93c9db0cff93f52b521d7420e43f6eda2784f b/tests/fuzz/corpus/5ba93c9db0cff93f52b521d7420e43f6eda2784f
new file mode 100644 (file)
index 0000000..f76dd23
Binary files /dev/null and b/tests/fuzz/corpus/5ba93c9db0cff93f52b521d7420e43f6eda2784f differ
diff --git a/tests/fuzz/corpus/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc b/tests/fuzz/corpus/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/tests/fuzz/test-fuzz.c b/tests/fuzz/test-fuzz.c
new file mode 100644 (file)
index 0000000..a62c326
--- /dev/null
@@ -0,0 +1,43 @@
+#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 "util.h"
+
+static void fuzz_parse_command(const char *buf)
+{
+       char **p = parse_command(buf);
+       if (p)
+               free(p);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *input, size_t size)
+{
+       char *p = NULL;
+       char *fields[] = { "sessionid", NULL, "path", NULL, "filename", NULL, "mimetype", NULL };
+       char *buf = calloc(1, size+1);
+       memcpy(buf, input, size);
+
+       urldecode(buf);
+       fuzz_parse_command(buf);
+       p = canonicalize_path(buf, size+1);
+       if (p)
+               free(p);
+
+       p = postdecode_fields(buf, size+1, fields, 4);
+       if (!p)
+               return 0;
+
+       free(buf);
+
+       return 0;
+}
diff --git a/util.c b/util.c
index 9eb7b485e5fa698b64da260326ffdd44044ad6ce..e8627589b49af5613f2fd3bf236cc17571012d7a 100644 (file)
--- a/util.c
+++ b/util.c
@@ -5,6 +5,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <stdio.h>
+
 #include "util.h"
 
 char **
@@ -80,12 +82,55 @@ parse_command(const char *cmdline)
        return argv;
 }
 
+char *
+postdecode_fields(char *postbuf, ssize_t len, char **fields, int n_fields)
+{
+       char *p;
+       int i, field, found = 0;
+
+       for (p = postbuf, i = 0; i <= len; i++)
+       {
+               if (postbuf[i] == '=')
+               {
+                       postbuf[i] = 0;
+
+                       for (field = 0; field < (n_fields * 2); field += 2)
+                       {
+                               if (!strcmp(p, fields[field]))
+                               {
+                                       fields[field + 1] = postbuf + i + 1;
+                                       found++;
+                               }
+                       }
+               }
+               else if (postbuf[i] == '&' || postbuf[i] == '\0')
+               {
+                       postbuf[i] = 0;
+
+                       if (found >= n_fields)
+                               break;
+
+                       p = postbuf + i + 1;
+               }
+       }
+
+       for (field = 0; field < (n_fields * 2); field += 2)
+       {
+               if (!urldecode(fields[field + 1]))
+               {
+                       free(postbuf);
+                       return NULL;
+               }
+       }
+
+       return postbuf;
+}
+
 char *
 postdecode(char **fields, int n_fields)
 {
        const char *var;
        char *p, *postbuf;
-       int i, field, found = 0;
        ssize_t len = 0, rlen = 0, content_length = 0;
 
        var = getenv("CONTENT_TYPE");
@@ -124,42 +169,7 @@ postdecode(char **fields, int n_fields)
                return NULL;
        }
 
-       for (p = postbuf, i = 0; i <= len; i++)
-       {
-               if (postbuf[i] == '=')
-               {
-                       postbuf[i] = 0;
-
-                       for (field = 0; field < (n_fields * 2); field += 2)
-                       {
-                               if (!strcmp(p, fields[field]))
-                               {
-                                       fields[field + 1] = postbuf + i + 1;
-                                       found++;
-                               }
-                       }
-               }
-               else if (postbuf[i] == '&' || postbuf[i] == '\0')
-               {
-                       postbuf[i] = 0;
-
-                       if (found >= n_fields)
-                               break;
-
-                       p = postbuf + i + 1;
-               }
-       }
-
-       for (field = 0; field < (n_fields * 2); field += 2)
-       {
-               if (!urldecode(fields[field + 1]))
-               {
-                       free(postbuf);
-                       return NULL;
-               }
-       }
-
-       return postbuf;
+       return postdecode_fields(postbuf, len, fields, n_fields);
 }
 
 char *
diff --git a/util.h b/util.h
index 0001195df38aff0cc00a62cd65f7d3ae7c060ec7..ecffe6c2bd7358f23ea6cf73109f0aa3ee7549bd 100644 (file)
--- a/util.h
+++ b/util.h
@@ -6,6 +6,7 @@
 
 char** parse_command(const char *cmdline);
 char* postdecode(char **fields, int n_fields);
+char* postdecode_fields(char *postbuf, ssize_t len, char **fields, int n_fields);
 char* canonicalize_path(const char *path, size_t len);
 bool urldecode(char *buf);
 char* datadup(const void *in, size_t len);