diff --git a/test/fuzz/README.md b/test/fuzz/README.md new file mode 100644 index 00000000..585cfa97 --- /dev/null +++ b/test/fuzz/README.md @@ -0,0 +1,38 @@ +# Fuzzing + +This directory contains code for fuzz testing libhttpserver with LLVM's [libFuzzer](http://llvm.org/docs/LibFuzzer.html). + +## Build the libraries + +Build libhttpserver and the dependent libraries with ASAN. +``` +export CC=clang +export CXX=clang++ +export CFLAGS="-O1 -fno-omit-frame-pointer -gline-tables-only -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link" +export CXXFLAGS="-O1 -fno-omit-frame-pointer -gline-tables-only -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link" + +cd libmicrohttpd-0.9.71/ +./configure +make && sudo make install + +cd ../libhttpserver +cd build +../configure +make && sudo make install +``` + +## Build the fuzz target +``` +cd libhttpserver/test/fuzz +clang++ $CXXFLAGS basic_fuzzer.cc -o basic_fuzzer -fsanitize=fuzzer,undefined /usr/local/lib/libhttpserver.a /usr/local/lib/libmicrohttpd.a -lgnutls +clang++ $CXXFLAGS ip_representation.cc -o ip_representation -fsanitize=fuzzer,undefined /usr/local/lib/libhttpserver.a /usr/local/lib/libmicrohttpd.a -lgnutls +``` + +## Run the fuzz target +``` +unzip basic_fuzzer_seed_corpus.zip +./basic_fuzzer corpus/ + +unzip ip_representation_seed_corpus.zip +./ip_representation ip_corpus/ +``` diff --git a/test/fuzz/basic_fuzzer.cc b/test/fuzz/basic_fuzzer.cc new file mode 100755 index 00000000..5541ec94 --- /dev/null +++ b/test/fuzz/basic_fuzzer.cc @@ -0,0 +1,180 @@ +/* + * Fuzzing test code for libhttpserver using LLVM's libFuzzer + * (http://llvm.org/docs/LibFuzzer.html) + * Refer README.md for build instructions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define HOST_IP "127.0.0.1" + +unsigned int get_port_no(void); + +using namespace httpserver; +unsigned int port; +webserver ws = create_webserver(get_port_no()); + +class fuzz_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + std::stringstream ss; + req.get_args(); + req.get_headers(); + req.get_footers(); + req.get_cookies(); + req.get_querystring(); + req.get_user(); + req.get_pass(); + req.get_digested_user(); + req.get_requestor(); + req.get_requestor_port(); + for (unsigned int i = 0; i < req.get_path_pieces().size(); i++) + ss << req.get_path_piece(i) << ","; + return std::shared_ptr(new string_response(ss.str(), 200)); + } +}hwr; + +class args_resource: public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("ARGS: " + + req.get_arg("arg1") + "and" + req.get_arg("arg2"))); + } +}agr; + +unsigned int get_port_no(void) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in address; + socklen_t len = sizeof(address); + + memset(&address, 0 ,sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr(HOST_IP); + address.sin_port = 0; //Use the next free port + + bind(fd, (struct sockaddr*) &address, sizeof(address)); + getsockname(fd, (struct sockaddr*) &address, &len); + port = ntohs(address.sin_port); + + printf("Using port %d\n", port); + close(fd); + return port; +} + +void quit(const char *msg) { + perror(msg); + exit(-1); +} + +int connect_server(void) { + struct sockaddr_in address; + int sfd, ret; + + sfd = socket(AF_INET, SOCK_STREAM, 0); + if (sfd < 0) + quit("Failed to open socket"); + + memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = inet_addr(HOST_IP); + +retry: + ret = connect(sfd, (struct sockaddr *)&address, sizeof(address)); + if (ret < 0) { + if (errno == EINTR) + goto retry; + quit("Failed to connect to server"); + } + + return sfd; +} + +void write_request(int sfd, const uint8_t *data, size_t size) { + std::string method = "PUT "; + std::string suffix = " HTTP/1.1\r\n\r\n"; + std::string str(reinterpret_cast(data), size); + std::string fstr = method + str + suffix; + const char *msg; + int bytes, sent = 0; + + size = fstr.length(); + msg = fstr.c_str(); + do { + bytes = write(sfd, msg + sent, size - sent); + if (bytes == 0) + break; + else if (bytes < 0) { + if (errno == EINTR) + continue; + quit("Failed to write HTTP request"); + } + sent += bytes; + } while (sent < size); +} + +void read_response(int sfd) { + char response[150]; + int bytes; + + bytes = read(sfd, response , 150); + if (bytes < 0) + return; +#if PRINT_RESPONSE + printf("%s\n", response); +#endif +} + +void cleanup(void) +{ + /* Stop the server */ + ws.stop(); +} + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) +{ + ws.register_resource("{arg1|[A-Z]+}/{arg2|(.*)}", &agr); + ws.register_resource(R"(.*)", &hwr); + + /* Start the server */ + ws.start(false); + + atexit(cleanup); + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + int sfd; + + if (memchr(data, '\n', size)) + return 0; + + if (memchr(data, '\r', size)) + return 0; + + /* Client -> connect to server */ + sfd = connect_server(); + + /* HTTP request and response*/ + write_request(sfd, data, size); + read_response(sfd); + + /* Client -> close connection */ + close(sfd); + + return 0; +} diff --git a/test/fuzz/basic_fuzzer_seed_corpus.zip b/test/fuzz/basic_fuzzer_seed_corpus.zip new file mode 100644 index 00000000..ae90e9d1 Binary files /dev/null and b/test/fuzz/basic_fuzzer_seed_corpus.zip differ diff --git a/test/fuzz/ip_representation.cc b/test/fuzz/ip_representation.cc new file mode 100644 index 00000000..1dda6de8 --- /dev/null +++ b/test/fuzz/ip_representation.cc @@ -0,0 +1,14 @@ +#include + +using namespace httpserver; +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (!size) + return 0; + std::string str(reinterpret_cast(data), size); + try { + http::ip_representation test(str); + } catch (std::exception &e) { + return 0; + } + return 0; +} diff --git a/test/fuzz/ip_representation_seed_corpus.zip b/test/fuzz/ip_representation_seed_corpus.zip new file mode 100644 index 00000000..c1df0409 Binary files /dev/null and b/test/fuzz/ip_representation_seed_corpus.zip differ