| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 | /********************************************************//*                                                      *//*   FUZZ TEST for civetweb.c                           *//*                                                      *//*   Copyright (c) 2015-2020 the CivetWeb developers    *//*                                                      *//*   This file contains test code for fuzz tests.       *//*   It should not be used in production code.          *//*                                                      *//********************************************************/#include "civetweb.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 <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;#define closesocket(a) (close(a))#endif // not _WIN32/* Port configuration */unsigned short PORT_NUM_HTTP = 0; /* set dynamically */#define TESTabort()                                                            \	{                                                                          \		fprintf(stderr, "!!! Precondition in test environment not met !!!\n"); \		fprintf(stderr, "!!! aborting fuzz test in line %u !!!", __LINE__);    \		abort();                                                               \	}/********************************************************//* Init CivetWeb server ... test with mock client       *//********************************************************/#if defined(TEST_FUZZ1) || defined(TEST_FUZZ2)static struct mg_context *ctx = 0;static const char *OPTIONS[] = {"listening_ports",                                "0", /* port: auto */                                "document_root",                                "fuzztest/docroot",                                NULL,                                NULL};static voidcivetweb_exit(void){	printf("CivetWeb server exit\n");	mg_stop(ctx);	ctx = 0;	test_sleep(5);}static voidcivetweb_init(void){	struct mg_callbacks callbacks;	struct mg_server_port ports[8];	memset(&callbacks, 0, sizeof(callbacks));	memset(&ports, 0, sizeof(ports));	ctx = mg_start(&callbacks, 0, OPTIONS);	if (!ctx) {		fprintf(stderr, "\nCivetWeb test server failed to start\n");		TESTabort();	}	int ret = mg_get_server_ports(ctx, 8, ports);	if (ret != 1) {		fprintf(stderr,		        "\nCivetWeb test server: cannot determine port number\n");		TESTabort();	}	if (ports[0].is_ssl != 0) {		fprintf(stderr,		        "\nCivetWeb fuzz test works on HTTP, not HTTPS.\n"		        "TLS library should be fuzzed separately.\n");		TESTabort();	}	PORT_NUM_HTTP = ports[0].port;	printf("CivetWeb server running on port %i\n", (int)PORT_NUM_HTTP);	/* Give server 5 seconds to start, before flooding with requests.	 * Don't know if this is required for fuzz-tests, but it was helpful	 * when testing starting/stopping the server multiple times in test	 * container environments. */	test_sleep(5);	atexit(civetweb_exit);}int LLVMFuzzerInitialize(int *argc, char ***argv);intLLVMFuzzerInitialize(int *argc, char ***argv){	// Silence unused args warning.	(void)(argc);	(void)(argv);	civetweb_init();	return 0;}#if defined(TEST_FUZZ1)static inttest_civetweb_client(const char *server,                     uint16_t port,                     int use_ssl,                     const char *uri){	/* Client var */	struct mg_connection *client;	char client_err_buf[256];	char client_data_buf[4096];	const struct mg_response_info *client_ri;	int64_t data_read;	int r;	client = mg_connect_client(	    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);		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));		if (!client) {			fprintf(stderr, "Retry: error\n");			return 1;		}		fprintf(stderr, "Retry: success\n");	}	mg_printf(client, "GET %s HTTP/1.0\r\n\r\n", uri);	r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);	if ((r < 0) || (0 != strcmp(client_err_buf, ""))) {		mg_close_connection(client);		return 1;	}	client_ri = mg_get_response_info(client);	if (client_ri == NULL) {		mg_close_connection(client);		return 1;	}	data_read = 0;	while (data_read < client_ri->content_length) {		/* store the first sizeof(client_data_buf) bytes		 * of the HTTP response. */		r = mg_read(client,		            client_data_buf + data_read,		            sizeof(client_data_buf) - (size_t)data_read);		if (r > 0) {			data_read += r;		}		/* buffer filled? */		if (sizeof(client_data_buf) == (size_t)data_read) {			/* ignore the rest */			while (r > 0) {				char trash[1024];				r = mg_read(client, trash, sizeof(trash));			}			break;		}	}	/* Nothing left to read */	r = mg_read(client, client_data_buf, sizeof(client_data_buf));	if (r != 0) {		mg_close_connection(client);		return 1;	}	mg_close_connection(client);	return 0;}static intLLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size){	static char URI[1024 * 64]; /* static, to avoid stack overflow */	if (size + 1 < sizeof(URI)) {		memcpy(URI, data, size);		URI[size] = 0;	} else {		return 1;	}	return test_civetweb_client("127.0.0.1", PORT_NUM_HTTP, 0, URI);}#endif#if defined(TEST_FUZZ2)static intLLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size){	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;	}	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(PORT_NUM_HTTP);	r = connect(sock, (struct sockaddr *)&sin, sizeof(sin));	if (r != 0) {		r = errno;		fprintf(stderr, "Error: Cannot connect [%s]\n", strerror(r));		closesocket(sock);		return 1;	}	char trash[1024];	r = send(sock, data, size, 0);	if (r != (int)size) {		fprintf(stderr, "Warning: %i bytes sent (TODO: Repeat)\n", r);	}	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) {		max_data_read = data_read;		printf("GOT data: %i\n", data_read);	}	return 0;}#endif#endif // defined(TEST_FUZZ1) || defined(TEST_FUZZ2)/********************************************************//* Init mock server ... test with CivetWeb client       *//********************************************************/#if defined(TEST_FUZZ3)struct tcp_func_prm {	SOCKET sock;};struct tRESPONSE {	char data[4096];	size_t size;} RESPONSE;volatile int mock_server_stop_flag = 0;static voidmock_server_exit(void){	printf("MOCK server exit\n");	mock_server_stop_flag = 1;	test_sleep(5);}static void *mock_server_thread(void *arg){	char req[1024 * 16];	SOCKET svr = (SOCKET)(-1);	/* Get thread parameters and free arg */	{		struct tcp_func_prm *ptcp_func_prm = (struct tcp_func_prm *)arg;		svr = ptcp_func_prm->sock;		free(arg);	}	mock_server_stop_flag = 0;	printf("MOCK server ready, sock %i\n", svr);next_request:	while (!mock_server_stop_flag) {		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);	}	return 0;}static voidmock_server_init(void){	int r;	int bind_success = 0;	SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);	if (sock == -1) {		r = errno;		fprintf(stderr, "Error: Cannot create socket [%s]\n", strerror(r));		TESTabort();	}	for (PORT_NUM_HTTP = 1024; PORT_NUM_HTTP != 0; PORT_NUM_HTTP++) {		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(PORT_NUM_HTTP);		r = bind(sock, (struct sockaddr *)&sin, sizeof(sin));		if (r == 0) {			bind_success = 1;			break;		}		r = errno;		fprintf(stderr, "Warning: Cannot bind [%s]\n", strerror(r));	}	if (!bind_success) {		fprintf(stderr, "Error: Cannot bind to any port\n");		closesocket(sock);		TESTabort();	}	printf("MOCK server running on port %i\n", (int)PORT_NUM_HTTP);	r = listen(sock, 128);	if (r != 0) {		r = errno;		fprintf(stderr, "Error: Cannot listen [%s]\n", strerror(r));		closesocket(sock);		TESTabort();	}	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);		TESTabort();	}	thread_prm->sock = sock;	(void)pthread_attr_init(&attr);	(void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);	result = pthread_create(&thread_id,	                        &attr,	                        mock_server_thread,	                        (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);		TESTabort();	}	test_sleep(3);	atexit(mock_server_exit);}int LLVMFuzzerInitialize(int *argc, char ***argv);intLLVMFuzzerInitialize(int *argc, char ***argv){	// Silence unused args warning.	(void)(argc);	(void)(argv);	mock_server_init();	return 0;}static intLLVMFuzzerTestOneInput_RESPONSE(const uint8_t *data, size_t size){	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", PORT_NUM_HTTP, 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);	(void)r;	(void)ri;	mg_close_connection(conn);	return 0;}#endif // defined(TEST_FUZZ3)/********************************************************//* MAIN for fuzztest                                    *//********************************************************/int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);// warning: no previous prototype for function 'LLVMFuzzerTestOneInput'intLLVMFuzzerTestOneInput(const uint8_t *data, size_t size){#if defined(TEST_FUZZ1)	/* fuzz target 1: different URI for HTTP/1 server */	LLVMFuzzerTestOneInput_URI(data, size);	return 0;#elif defined(TEST_FUZZ2)	/* fuzz target 2: different requests for HTTP/1 server */	LLVMFuzzerTestOneInput_REQUEST(data, size);	return 0;#elif defined(TEST_FUZZ3)	/* fuzz target 3: different responses for HTTP/1 client */	LLVMFuzzerTestOneInput_RESPONSE(data, size);	return 0;#elif defined(TEST_FUZZ4)#error "Only useful in HTTP/2 feature branch"	/* fuzz target 4: different requests for HTTP/2 server */	LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);	return 0;#elif defined(TEST_FUZZ5)	/* fuzz target 5: calling an internal server test function,	 *                bypassing network sockets */	LLVMFuzzerTestOneInput_process_new_connection(data, size);	return 0;#else/* planned targets */#error "Unknown fuzz target"#endif}
 |