123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- #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
- /* Port configuration */
- #define PORT_NUM_HTTP 8081
- #define PORT_STR_HTTPHTTPS "8081,8443s"
- #define TESTabort() {fprintf(stderr, "!!! aborting fuzz test in line %u !!!", __LINE__); abort();}
- static uint64_t call_count = 0;
- static struct mg_context *ctx;
- static const char *OPTIONS[] = {"listening_ports",
- PORT_STR_HTTPHTTPS,
- "document_root",
- "fuzztest/docroot",
- "ssl_certificate",
- "resources/cert/server.pem",
- NULL,
- NULL};
- static void
- init_civetweb(void)
- {
- struct mg_callbacks callbacks;
- memset(&callbacks, 0, sizeof(callbacks));
- ctx = mg_start(&callbacks, 0, OPTIONS);
- if (!ctx) {
- fprintf(stderr, "\nCivetWeb test server failed to start\n");
- TESTabort();
- }
- /* 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);
- }
- 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));
- TESTabort();
- }
- 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) {
- r = errno;
- fprintf(stderr, "Error: Cannot bind [%s]\n", strerror(r));
- closesocket(sock);
- TESTabort();
- }
- 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, 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);
- TESTabort();
- }
- test_sleep(5);
- }
- static int
- test_http_request(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 int
- LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
- {
- static char URI[1024 * 64]; /* static, to avoid stack overflow */
- if (call_count == 0) {
- memset(URI, 0, sizeof(URI));
- init_civetweb();
- }
- call_count++;
- if (size < sizeof(URI)) {
- memcpy(URI, data, size);
- URI[size] = 0;
- } else {
- return 1;
- }
- return test_http_request("127.0.0.1", PORT_NUM_HTTP, 0, URI);
- }
- static int
- LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
- {
- if (call_count == 0) {
- init_civetweb();
- }
- 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;
- }
- 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 != 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);
- }
- }
- 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", 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);
- mg_close_connection(conn);
- return 0;
- }
- /* MAIN for fuzztest */
- 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);
- #elif defined(TEST_FUZZ3)
- /* fuzz target 3: different responses for HTTP/1 client */
- return LLVMFuzzerTestOneInput_RESPONSE(data, size);
- #elif defined(TEST_FUZZ4)
- #error "Only useful in HTTP/2 feature branch"
- /* fuzz target 4: different requests for HTTP/2 server */
- return LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
- #elif defined(TEST_FUZZ5)
- /* fuzz target 5: calling an internal server test function,
- * bypassing network sockets */
- return LLVMFuzzerTestOneInput_process_new_connection(data, size);
- #else
- /* planned targets */
- #error "Unknown fuzz target"
- #endif
- }
|