Skip to content

Commit 03b2d46

Browse files
committed
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.
1 parent 7cb4eb8 commit 03b2d46

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

test/fuzz/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Fuzzing
2+
3+
This directory contains code for fuzz testing libhttpserver with LLVM's [libFuzzer](http://llvm.org/docs/LibFuzzer.html).
4+
5+
## Build the libraries
6+
7+
Build libhttpserver and the dependent libraries with ASAN.
8+
```
9+
export CC=clang
10+
export CXX=clang++
11+
export CFLAGS="-O1 -fno-omit-frame-pointer -gline-tables-only -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link"
12+
export CXXFLAGS="-O1 -fno-omit-frame-pointer -gline-tables-only -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link"
13+
14+
cd libmicrohttpd-0.9.71/
15+
./configure
16+
make && sudo make install
17+
18+
cd ../libhttpserver
19+
cd build
20+
../configure
21+
make && sudo make install
22+
```
23+
24+
## Build the fuzz target
25+
```
26+
cd libhttpserver/test/fuzz
27+
clang++ $CXXFLAGS basic_fuzzer.cc -o basic_fuzzer -fsanitize=fuzzer,undefined /usr/local/lib/libhttpserver.a /usr/local/lib/libmicrohttpd.a -lgnutls
28+
```
29+
30+
## Run the fuzz target
31+
```
32+
unzip basic_fuzzer_seed_corpus.zip
33+
./basic_fuzzer corpus/
34+
```

test/fuzz/basic_fuzzer.cc

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Fuzzing test code for libhttpserver using LLVM's libFuzzer
3+
* (http://llvm.org/docs/LibFuzzer.html)
4+
* Refer README.md for build instructions
5+
*/
6+
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <unistd.h>
10+
#include <string.h>
11+
#include <stdint.h>
12+
#include <stddef.h>
13+
#include <netdb.h>
14+
#include <sys/socket.h>
15+
#include <netinet/in.h>
16+
#include <arpa/inet.h>
17+
18+
#include <iostream>
19+
#include <sstream>
20+
#include <httpserver.hpp>
21+
22+
#define HOST_IP "127.0.0.1"
23+
24+
unsigned int get_port_no(void);
25+
26+
using namespace httpserver;
27+
unsigned int port;
28+
webserver ws = create_webserver(get_port_no());
29+
30+
class fuzz_resource : public http_resource {
31+
public:
32+
const std::shared_ptr<http_response> render(const http_request& req) {
33+
std::stringstream ss;
34+
for (unsigned int i = 0; i < req.get_path_pieces().size(); i++)
35+
ss << req.get_path_piece(i) << ",";
36+
return std::shared_ptr<http_response>(new string_response(ss.str(), 200));
37+
}
38+
}hwr;
39+
40+
class args_resource: public http_resource {
41+
public:
42+
const std::shared_ptr<http_response> render(const http_request& req) {
43+
return std::shared_ptr<http_response>(new string_response("ARGS: " +
44+
req.get_arg("arg1") + "and" + req.get_arg("arg2")));
45+
}
46+
}agr;
47+
48+
unsigned int get_port_no(void) {
49+
int fd = socket(AF_INET, SOCK_STREAM, 0);
50+
struct sockaddr_in address;
51+
socklen_t len = sizeof(address);
52+
53+
memset(&address, 0 ,sizeof(address));
54+
address.sin_family = AF_INET;
55+
address.sin_addr.s_addr = inet_addr(HOST_IP);
56+
address.sin_port = 0; //Use the next free port
57+
58+
bind(fd, (struct sockaddr*) &address, sizeof(address));
59+
getsockname(fd, (struct sockaddr*) &address, &len);
60+
port = ntohs(address.sin_port);
61+
62+
printf("Using port %d\n", port);
63+
close(fd);
64+
return port;
65+
}
66+
67+
void quit(const char *msg) {
68+
perror(msg);
69+
exit(-1);
70+
}
71+
72+
int connect_server(void) {
73+
struct sockaddr_in address;
74+
int sfd, ret;
75+
76+
sfd = socket(AF_INET, SOCK_STREAM, 0);
77+
if (sfd < 0)
78+
quit("Failed to open socket");
79+
80+
memset(&address, 0, sizeof(address));
81+
address.sin_family = AF_INET;
82+
address.sin_port = htons(port);
83+
address.sin_addr.s_addr = inet_addr(HOST_IP);
84+
85+
retry:
86+
ret = connect(sfd, (struct sockaddr *)&address, sizeof(address));
87+
if (ret < 0) {
88+
if (errno == EINTR)
89+
goto retry;
90+
quit("Failed to connect to server");
91+
}
92+
93+
return sfd;
94+
}
95+
96+
void write_request(int sfd, const uint8_t *data, size_t size) {
97+
std::string method = "PUT ";
98+
std::string suffix = " HTTP/1.1\r\n\r\n";
99+
std::string str(reinterpret_cast<const char *>(data), size);
100+
std::string fstr = method + str + suffix;
101+
const char *msg;
102+
int bytes, sent = 0;
103+
104+
size = fstr.length();
105+
msg = fstr.c_str();
106+
do {
107+
bytes = write(sfd, msg + sent, size - sent);
108+
if (bytes == 0)
109+
break;
110+
else if (bytes < 0) {
111+
if (errno == EINTR)
112+
continue;
113+
quit("Failed to write HTTP request");
114+
}
115+
sent += bytes;
116+
} while (sent < size);
117+
}
118+
119+
void read_response(int sfd) {
120+
char response[150];
121+
int bytes;
122+
123+
bytes = read(sfd, response , 150);
124+
if (bytes < 0)
125+
return;
126+
#if PRINT_RESPONSE
127+
printf("%s\n", response);
128+
#endif
129+
}
130+
131+
void cleanup(void)
132+
{
133+
/* Stop the server */
134+
ws.stop();
135+
}
136+
137+
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
138+
{
139+
ws.register_resource("{arg1|[A-Z]+}/{arg2|(.*)}", &agr);
140+
ws.register_resource(R"(.*)", &hwr);
141+
142+
/* Start the server */
143+
ws.start(false);
144+
145+
atexit(cleanup);
146+
147+
return 0;
148+
}
149+
150+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
151+
int sfd;
152+
153+
if (memchr(data, '\n', size))
154+
return 0;
155+
156+
if (memchr(data, '\r', size))
157+
return 0;
158+
159+
/* Client -> connect to server */
160+
sfd = connect_server();
161+
162+
/* HTTP request and response*/
163+
write_request(sfd, data, size);
164+
read_response(sfd);
165+
166+
/* Client -> close connection */
167+
close(sfd);
168+
169+
return 0;
170+
}
467 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)