Browse Source

Add fuzz test for client

bel2125 5 years ago
parent
commit
8f28f092d4

+ 3 - 0
.gitattributes

@@ -1,3 +1,6 @@
+# Binary files
+*.bin    binary
+
 # Auto detect text files and perform LF normalization
 * -text
 

+ 6 - 1
fuzztest/README.txt

@@ -13,13 +13,18 @@ Second fuzz target: vary HTTP1 request for HTTP1 server
 - mv civetweb civetweb_fuzz2
 - sudo ./civetweb_fuzz2 -max_len=2048 -dict=fuzztest/http1.dict fuzztest/http1/
 
+Third fuzz target: vary HTTP1 response for HTTP1 client API
+- make WITH_ALL=1 TEST_FUZZ=3
+- mv civetweb civetweb_fuzz3
+- sudo ./civetweb_fuzz2 -max_len=2048 -dict=fuzztest/http1.dict fuzztest/http1c/
+
+
 
 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?

+ 175 - 3
fuzztest/fuzzmain.c

@@ -13,6 +13,7 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#include <pthread.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 typedef int SOCKET;
@@ -55,6 +56,135 @@ init_civetweb(void)
 }
 
 
+struct tcp_func_prm {
+	SOCKET sock;
+};
+
+
+struct tRESPONSE {
+	char data[4096];
+	size_t size;
+} RESPONSE;
+
+
+static void *
+tcp_func(void *arg)
+{
+	char req[1024 * 16];
+	struct tcp_func_prm *ptcp_func_prm = (struct tcp_func_prm *)arg;
+	SOCKET svr = ptcp_func_prm->sock;
+	printf("Server ready, sock %i\n", svr);
+
+next_request : {
+	struct sockaddr_in cliadr;
+	socklen_t adrlen = sizeof(cliadr);
+	int buf_filled = 0;
+	int req_ready = 0;
+
+	memset(&cliadr, 0, sizeof(cliadr));
+
+	SOCKET cli = accept(svr, (struct sockaddr *)&cliadr, &adrlen);
+
+	if (cli == -1) {
+		int er = errno;
+		fprintf(stderr, "Error: Accept failed [%s]\n", strerror(er));
+		test_sleep(1);
+		goto next_request;
+	}
+
+	/* Read request */
+	do {
+		int r = recv(cli, req + buf_filled, sizeof(req) - buf_filled - 1, 0);
+		if (r > 0) {
+			buf_filled += r;
+			req[buf_filled] = 0;
+			if (strstr(req, "\r\n\r\n") != NULL) {
+				req_ready = 1;
+			}
+		} else {
+			/* some error */
+			int er = errno;
+			fprintf(stderr, "Error: Recv failed [%s]\n", strerror(er));
+			test_sleep(1);
+			goto next_request;
+		}
+	} while (!req_ready);
+
+	/* Request is complete here.
+	 * Now send response */
+	send(cli, RESPONSE.data, RESPONSE.size, MSG_NOSIGNAL);
+
+	/* Close connection. */
+	shutdown(cli, SHUT_RDWR);
+	closesocket(cli);
+
+	/* done */
+	goto next_request;
+}
+
+	free(arg);
+}
+
+
+static void
+init_tcp(void)
+{
+	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));
+		abort();
+	}
+	struct sockaddr_in sin;
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = inet_addr("127.0.0.1");
+	sin.sin_port = htons(8080);
+	r = bind(sock, (struct sockaddr *)&sin, sizeof(sin));
+	if (r != 0) {
+		r = errno;
+		fprintf(stderr, "Error: Cannot bind [%s]\n", strerror(r));
+		closesocket(sock);
+		abort();
+	}
+
+	r = listen(sock, 128);
+	if (r != 0) {
+		r = errno;
+		fprintf(stderr, "Error: Cannot listen [%s]\n", strerror(r));
+		closesocket(sock);
+		abort();
+	}
+
+	pthread_t thread_id;
+	pthread_attr_t attr;
+	int result;
+	struct tcp_func_prm *thread_prm;
+
+	thread_prm = (struct tcp_func_prm *)malloc(sizeof(struct tcp_func_prm));
+	if (!thread_prm) {
+		fprintf(stderr, "Error: Out of memory\n");
+		closesocket(sock);
+		abort();
+	}
+	thread_prm->sock = sock;
+
+	(void)pthread_attr_init(&attr);
+	(void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	result = pthread_create(&thread_id, &attr, tcp_func, (void *)thread_prm);
+	(void)pthread_attr_destroy(&attr);
+	if (result != 0) {
+		r = errno;
+		fprintf(stderr, "Error: Cannot create thread [%s]\n", strerror(r));
+		closesocket(sock);
+		abort();
+	}
+
+	test_sleep(5);
+}
+
+
 static int
 test_http_request(const char *server,
                   uint16_t port,
@@ -219,6 +349,42 @@ LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
 	}
 }
 
+
+static int
+LLVMFuzzerTestOneInput_RESPONSE(const uint8_t *data, size_t size)
+{
+	if (call_count == 0) {
+		init_tcp();
+	}
+	call_count++;
+
+	if (size > sizeof(RESPONSE.data)) {
+		return 1;
+	}
+
+	memcpy(RESPONSE.data, data, size);
+	RESPONSE.size = size;
+
+	char errbuf[256];
+
+	struct mg_connection *conn =
+	    mg_connect_client("127.0.0.1", 8080, 0, errbuf, sizeof(errbuf));
+	if (!conn) {
+		printf("Connect error: %s\n", errbuf);
+		test_sleep(1);
+		return 1;
+	}
+	mg_printf(conn, "GET / HTTP/1.0\r\n\r\n");
+
+	int r = mg_get_response(conn, errbuf, sizeof(errbuf), 1000);
+	const struct mg_response_info *ri = mg_get_response_info(conn);
+
+	mg_close_connection(conn);
+
+	return 0;
+}
+
+
 int
 LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
@@ -228,12 +394,18 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 #elif defined(TEST_FUZZ2)
 	/* fuzz target 2: different requests for HTTP/1 server */
 	return LLVMFuzzerTestOneInput_REQUEST(data, size);
-#else
-/* planned targets */
-/* fuzz target 3: different responses for HTTP/1 client */
+#elif defined(TEST_FUZZ3)
+	/* fuzz target 3: different responses for HTTP/1 client */
+	return LLVMFuzzerTestOneInput_RESPONSE(data, size);
+#elif defined(TEST_FUZZ4)
 /* fuzz target 4: different requests for HTTP/2 server */
+#error "Only useful in HTTP/2 feature branch"
+#elif defined(TEST_FUZZ5)
 /* fuzz target 5: calling an internal server test function,
  *                bypassing network sockets */
+#error "Not implemented yet"
+#else
+/* planned targets */
 #error "Unknown fuzz target"
 #endif
 }

+ 0 - 0
fuzztest/http1/GET_root → fuzztest/http1/GET_root.bin


+ 0 - 0
fuzztest/http1/GET_test → fuzztest/http1/GET_test.bin


+ 18 - 0
fuzztest/http1c/RESULT_200_1.bin

@@ -0,0 +1,18 @@
+HTTP/1.1 200 OK
+Date: Mon, 23 May 2005 22:38:34 GMT
+Content-Type: text/html; charset=UTF-8
+Content-Length: 155
+Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
+Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
+ETag: "3f80f-1b6-3e1cb03b"
+Accept-Ranges: bytes
+Connection: close
+
+<html>
+  <head>
+    <title>An Example Page</title>
+  </head>
+  <body>
+    <p>Hello World, this is a very simple HTML document.</p>
+  </body>
+</html>

+ 2 - 0
fuzztest/http1c/RESULT_200_2.bin

@@ -0,0 +1,2 @@
+HTTP/1.0 200 OK
+

+ 2 - 0
fuzztest/http1c/RESULT_400_1.bin

@@ -0,0 +1,2 @@
+HTTP/1.0 400 Not Found
+