From 03b2d46a0cd5ecc3a391723a4540e3adb896b12b Mon Sep 17 00:00:00 2001 From: Shilpasri G Bhat Date: Sun, 29 Nov 2020 15:34:39 -0600 Subject: [PATCH 1/2] test: Add fuzzing test for libhttpserver Add fuzz target for HTTP server using libFuzzer. The fuzz target sends HTTP request to server using the fuzzed input from libFuzzer. --- test/fuzz/README.md | 34 +++++ test/fuzz/basic_fuzzer.cc | 170 +++++++++++++++++++++++++ test/fuzz/basic_fuzzer_seed_corpus.zip | Bin 0 -> 467 bytes 3 files changed, 204 insertions(+) create mode 100644 test/fuzz/README.md create mode 100755 test/fuzz/basic_fuzzer.cc create mode 100644 test/fuzz/basic_fuzzer_seed_corpus.zip diff --git a/test/fuzz/README.md b/test/fuzz/README.md new file mode 100644 index 00000000..1873d28d --- /dev/null +++ b/test/fuzz/README.md @@ -0,0 +1,34 @@ +# 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 +``` + +## Run the fuzz target +``` +unzip basic_fuzzer_seed_corpus.zip +./basic_fuzzer corpus/ +``` diff --git a/test/fuzz/basic_fuzzer.cc b/test/fuzz/basic_fuzzer.cc new file mode 100755 index 00000000..e64e1d07 --- /dev/null +++ b/test/fuzz/basic_fuzzer.cc @@ -0,0 +1,170 @@ +/* + * 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; + 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 0000000000000000000000000000000000000000..ae90e9d1d437ec1b190b9c9fc49ef1bc8c3f0830 GIT binary patch literal 467 zcmWIWW@h1H0D*!$gJvZ$>6LW?a6K0GbH`0t{~*K{VWttPnq<8H;Qf zrVk;8F#?TQ(g-pQY8cSJK+{0}#cCQZk0P7afiMl|ZJ=pbyd2=o$_7-%zzl>hf%Fv+ GhXDXEqFbf_ literal 0 HcmV?d00001 From aa058fe54ea5c4971de04ba0d1258981f88b0902 Mon Sep 17 00:00:00 2001 From: Shilpasri G Bhat Date: Sat, 12 Dec 2020 18:34:21 -0600 Subject: [PATCH 2/2] Improve fuzz test coverage Add fuzz target and corpus for testing ip_representation() API --- test/fuzz/README.md | 4 ++++ test/fuzz/basic_fuzzer.cc | 10 ++++++++++ test/fuzz/ip_representation.cc | 14 ++++++++++++++ test/fuzz/ip_representation_seed_corpus.zip | Bin 0 -> 500 bytes 4 files changed, 28 insertions(+) create mode 100644 test/fuzz/ip_representation.cc create mode 100644 test/fuzz/ip_representation_seed_corpus.zip diff --git a/test/fuzz/README.md b/test/fuzz/README.md index 1873d28d..585cfa97 100644 --- a/test/fuzz/README.md +++ b/test/fuzz/README.md @@ -25,10 +25,14 @@ make && sudo make install ``` 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 index e64e1d07..5541ec94 100755 --- a/test/fuzz/basic_fuzzer.cc +++ b/test/fuzz/basic_fuzzer.cc @@ -31,6 +31,16 @@ 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)); 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 0000000000000000000000000000000000000000..c1df0409ddd5741b392926292e47c8c097707ef2 GIT binary patch literal 500 zcmWIWW@h1H0D+nbJ%L~bl;C2JVaP0qPtGqYC@t0x4dG;9&W^eopAW>P72FJrEH9WD z7{EjT+>94pJ%PU`T)E2wGy{aWQOqy|8}cOJYJ3T%A%>Pldd4Ptre=EP76=1h0}Y&Z zq&G$gXc!2i8fXMI@D+9gt*p|3zzSrpfu50`p%KtnMkYCCT)ve68V>>j3~wDlG_v1W zA%4g3H^MwjpF_-J1e&y@(FMgkU^oB`1cd`012KJ$Y~WNB1A*ZHG!PUH7zVPk0aY?E N1K}qi{RYHg002%@Z8iV^ literal 0 HcmV?d00001