mirror of
https://github.com/civetweb/civetweb
synced 2025-03-28 21:13:27 +00:00
First draft for fuzz test (#893)
This commit is contained in:
parent
c58f09a9fe
commit
9c96991d7e
4
Makefile
4
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
|
||||
|
@ -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
fuzztest/README.txt
Normal file
25
fuzztest/README.txt
Normal file
@ -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?
|
@ -1,20 +1,20 @@
|
||||
#include "civetweb.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.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
fuzztest/http1.dict
Normal file
55
fuzztest/http1.dict
Normal file
@ -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"
|
Loading…
x
Reference in New Issue
Block a user