Просмотр исходного кода

First draft for fuzz test (#893)

bel2125 5 лет назад
Родитель
Сommit
9c96991d7e
9 измененных файлов с 127 добавлено и 39 удалено
  1. 2 2
      Makefile
  2. 2 0
      format.bat
  3. 25 0
      fuzztest/README.txt
  4. 0 0
      fuzztest/docroot/test.txt
  5. 43 37
      fuzztest/fuzzmain.c
  6. 55 0
      fuzztest/http1.dict
  7. 0 0
      fuzztest/http1/GET_root
  8. 0 0
      fuzztest/http1/GET_test
  9. 0 0
      fuzztest/url/test_txt

+ 2 - 2
Makefile

@@ -79,8 +79,8 @@ else ifdef TEST_FUZZ
   CFLAGS += -g -fsanitize=address,fuzzer
   CC = clang
   CXX = clang++
-  BUILD_DIRS += $(BUILD_DIR)/fuzz
-  APP_SOURCES = fuzz/fuzzmain.c
+  BUILD_DIRS += $(BUILD_DIR)/fuzztest
+  APP_SOURCES = fuzztest/fuzzmain.c
   OBJECTS = $(LIB_SOURCES:.c=.o) $(APP_SOURCES:.c=.o) 
   CFLAGS += -DTEST_FUZZ$(TEST_FUZZ)
 else

+ 2 - 0
format.bat

@@ -32,6 +32,8 @@ clang-format -i unittest/timertest.c
 clang-format -i unittest/civetweb_check.h
 clang-format -i unittest/main.c
 
+clang-format -i fuzztest/fuzzmain.c
+
 clang-format -i examples/embedded_c/embedded_c.c
 clang-format -i examples/rest/rest.c
 

+ 25 - 0
fuzztest/README.txt

@@ -0,0 +1,25 @@
+For fuzz testing civetweb, perform the following steps:
+
+- Switch to civetweb root directory
+- make clean
+
+First fuzz target: vary URI for HTTP1 server
+- make WITH_ALL=1 TEST_FUZZ=1
+- mv civetweb civetweb_fuzz1
+- sudo ./civetweb_fuzz1 -max_len=2048 fuzztest/url/
+
+Second fuzz target: vary HTTP1 request for HTTP1 server
+- make WITH_ALL=1 TEST_FUZZ=2
+- mv civetweb civetweb_fuzz2
+- sudo ./civetweb_fuzz2 -max_len=2048 -dict=fuzztest/http1.dict fuzztest/http1/
+
+
+Open issues:
+ * Need "sudo" for container? (ASAN seems to needs it on WSL test)
+ * let "make" create "civetweb_fuzz#" instead of "mv"
+ * useful initial corpus and directory
+ * Planned additional fuzz test: 
+  * vary HTTP1 response for HTTP1 client API
+  * vary HTTP2 request for HTTP2 server (in HTTP2 feature branch)
+  * use internal function to bypass socket (bottleneck)
+ * where to put fuzz corpus?

+ 0 - 0
fuzz/docroot/test.txt → fuzztest/docroot/test.txt


+ 43 - 37
fuzz/fuzzmain.c → fuzztest/fuzzmain.c

@@ -1,20 +1,20 @@
 #include "civetweb.h"
+#include <errno.h>
 #include <stdint.h>
-#include <string.h>
 #include <stdlib.h>
-#include <errno.h>
+#include <string.h>
 
 #if defined(_WIN32)
 #error "Currently not supported"
 #else
-	
+
 #include <unistd.h>
 #define test_sleep(x) (sleep(x))
-#include <sys/types.h>
-#include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/types.h>
 typedef int SOCKET;
 #define closesocket(a) (close(a))
 
@@ -24,17 +24,14 @@ typedef int SOCKET;
 static uint64_t call_count = 0;
 
 static struct mg_context *ctx;
-static const char *OPTIONS[] = {
-	"listening_ports", "8080,8443s",
-#ifdef _WIN32
-	"document_root",   "fuzz\\docroot",
-	"ssl_certificate", "resources\\cert\\server.pem",
-#else
-	"document_root",   "fuzz/docroot",
-	"ssl_certificate", "resources/cert/server.pem",
-#endif
-	NULL, NULL
-};
+static const char *OPTIONS[] = {"listening_ports",
+                                "8080,8443s",
+                                "document_root",
+                                "fuzztest/docroot",
+                                "ssl_certificate",
+                                "resources/cert/server.pem",
+                                NULL,
+                                NULL};
 
 
 static void
@@ -44,7 +41,7 @@ init_civetweb(void)
 	memset(&callbacks, 0, sizeof(callbacks));
 
 	ctx = mg_start(&callbacks, 0, OPTIONS);
-	
+
 	if (!ctx) {
 		fprintf(stderr, "\nCivetWeb test server failed to start\n");
 		abort();
@@ -76,22 +73,23 @@ test_http_request(const char *server,
 	    server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
 
 	if ((client == NULL) || (0 != strcmp(client_err_buf, ""))) {
-		fprintf(stderr, "%s connection to server [%s] port [%u] failed: [%s]\n",
-		             use_ssl ? "HTTPS" : "HTTP",
-		             server,
-		             port,
-		             client_err_buf);
+		fprintf(stderr,
+		        "%s connection to server [%s] port [%u] failed: [%s]\n",
+		        use_ssl ? "HTTPS" : "HTTP",
+		        server,
+		        port,
+		        client_err_buf);
 		if (client) {
 			mg_close_connection(client);
 		}
-		
+
 		/* In heavy fuzz testing, sometimes we run out of available sockets.
 		 * Wait for some seconds, and retry. */
 		test_sleep(5);
 
 		/* retry once */
 		client = mg_connect_client(
-			server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
+		    server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
 		if (!client) {
 			fprintf(stderr, "Retry: error\n");
 			return 1;
@@ -105,7 +103,7 @@ test_http_request(const char *server,
 
 	if ((r < 0) || (0 != strcmp(client_err_buf, ""))) {
 		mg_close_connection(client);
-		return 1;		
+		return 1;
 	}
 
 	client_ri = mg_get_response_info(client);
@@ -124,7 +122,7 @@ test_http_request(const char *server,
 		if (r > 0) {
 			data_read += r;
 		}
-		
+
 		/* buffer filled? */
 		if (sizeof(client_data_buf) == (size_t)data_read) {
 			/* ignore the rest */
@@ -165,7 +163,7 @@ LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
 	} else {
 		return 1;
 	}
-	
+
 	printf("URI: %s\n", URI);
 
 	return test_http_request("127.0.0.1", 8080, 0, URI);
@@ -178,14 +176,14 @@ LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
 	if (call_count == 0) {
 		init_civetweb();
 	}
-	call_count++;	
-	
+	call_count++;
+
 	int r;
 	SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);
 	if (sock == -1) {
 		r = errno;
 		fprintf(stderr, "Error: Cannot create socket [%s]\n", strerror(r));
-		return 1;		
+		return 1;
 	}
 	struct sockaddr_in sin;
 	memset(&sin, 0, sizeof(sin));
@@ -199,35 +197,43 @@ LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
 		closesocket(sock);
 		return 1;
 	}
-	
+
 	char trash[1024];
 	r = send(sock, data, size, 0);
 	if (r != size) {
 		fprintf(stderr, "Warning: %i bytes sent (TODO: Repeat)\n", r);
 	}
 
-	int data_read = 0; 
+	int data_read = 0;
 	while ((r = recv(sock, trash, sizeof(trash), 0)) > 0) {
 		data_read += r;
 	};
-	
+
 	shutdown(sock, SHUT_RDWR);
 	closesocket(sock);
-	
+
 	static int max_data_read = 0;
-	if (data_read>max_data_read) {
+	if (data_read > max_data_read) {
 		max_data_read = data_read;
 		printf("GOT data: %i\n", data_read);
 	}
 }
 
-int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
 #if defined(TEST_FUZZ1)
+	/* fuzz target 1: different URI for HTTP/1 server */
 	return LLVMFuzzerTestOneInput_URI(data, size);
 #elif defined(TEST_FUZZ2)
+	/* fuzz target 2: different requests for HTTP/1 server */
 	return LLVMFuzzerTestOneInput_REQUEST(data, size);
 #else
-	#error "Unknown fuzz target"
+/* planned targets */
+/* fuzz target 3: different responses for HTTP/1 client */
+/* fuzz target 4: different requests for HTTP/2 server */
+/* fuzz target 5: calling an internal server test function,
+ *                bypassing network sockets */
+#error "Unknown fuzz target"
 #endif
 }

+ 55 - 0
fuzztest/http1.dict

@@ -0,0 +1,55 @@
+"/"
+"/test.txt"
+"/test.html"
+"*"
+"GET"
+"POST"
+"PUT"
+"DELETE"
+"accept-charset"
+"accept-encoding"
+"accept-language"
+"accept-ranges"
+"accept"
+"access-control-allow-origin"
+"age"
+"allow"
+"authorization"
+"cache-control"
+"content-encoding"
+"content-disposition"
+"content-language"
+"content-length"
+"content-location"
+"content-range"
+"content-type"
+"cookie"
+"date"
+"etag"
+"expect"
+"expires"
+"from"
+"host"
+"if-match"
+"if-modified-since"
+"if-none-match"
+"if-range"
+"if-unmodified-since"
+"last-modified"
+"link"
+"location"
+"max-forwards"
+"proxy-authenticate"
+"proxy-authorization"
+"range"
+"referer"
+"refresh"
+"retry-after"
+"server"
+"set-cookie"
+"strict-transport-security"
+"transfer-encoding"
+"user-agent"
+"vary"
+"via"
+"www-authenticate"

+ 0 - 0
fuzz/http1/GET_root → fuzztest/http1/GET_root


+ 0 - 0
fuzz/http1/GET_test → fuzztest/http1/GET_test


+ 0 - 0
fuzz/url/test_txt → fuzztest/url/test_txt