Skip to content

Add fuzzing test for libhttpserver and submit to oss-fuzz #213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions test/fuzz/README.md
Original file line number Diff line number Diff line change
@@ -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/
```
180 changes: 180 additions & 0 deletions test/fuzz/basic_fuzzer.cc
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <iostream>
#include <sstream>
#include <httpserver.hpp>

#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<http_response> 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<http_response>(new string_response(ss.str(), 200));
}
}hwr;

class args_resource: public http_resource {
public:
const std::shared_ptr<http_response> render(const http_request& req) {
return std::shared_ptr<http_response>(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<const char *>(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;
}
Binary file added test/fuzz/basic_fuzzer_seed_corpus.zip
Binary file not shown.
14 changes: 14 additions & 0 deletions test/fuzz/ip_representation.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <httpserver.hpp>

using namespace httpserver;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (!size)
return 0;
std::string str(reinterpret_cast<const char *>(data), size);
try {
http::ip_representation test(str);
} catch (std::exception &e) {
return 0;
}
return 0;
}
Binary file added test/fuzz/ip_representation_seed_corpus.zip
Binary file not shown.