• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "libhfnetdriver/netdriver.h"
2 
3 #include <arpa/inet.h>
4 #include <errno.h>
5 #include <inttypes.h>
6 #include <netinet/in.h>
7 #include <netinet/tcp.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/socket.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #if defined(_HF_ARCH_LINUX)
17 #include <sched.h>
18 #endif /* defined(_HF_ARCH_LINUX) */
19 
20 #include "honggfuzz.h"
21 #include "libhfcommon/common.h"
22 #include "libhfcommon/files.h"
23 #include "libhfcommon/log.h"
24 #include "libhfcommon/ns.h"
25 #include "libhfcommon/util.h"
26 
27 __attribute__((visibility("default"))) __attribute__((used))
28 const char *const LIBHFNETDRIVER_module_netdriver = _HF_NETDRIVER_SIG;
29 
30 #define HFND_TCP_PORT_ENV "HFND_TCP_PORT"
31 #define HFND_SKIP_FUZZING_ENV "HFND_SKIP_FUZZING"
32 
33 static char *initial_server_argv[] = {"fuzzer", NULL};
34 
35 static struct {
36     int argc_server;
37     char **argv_server;
38     uint16_t tcp_port;
39     sa_family_t sa_family;
40 } hfnd_globals = {
41     .argc_server = 1,
42     .argv_server = initial_server_argv,
43     .tcp_port = 0,
44     .sa_family = AF_UNSPEC,
45 };
46 
47 extern int HonggfuzzNetDriver_main(int argc, char **argv);
48 
netDriver_mainProgram(void * unused HF_ATTR_UNUSED)49 static void *netDriver_mainProgram(void *unused HF_ATTR_UNUSED) {
50     int ret = HonggfuzzNetDriver_main(hfnd_globals.argc_server, hfnd_globals.argv_server);
51     LOG_I("Honggfuzz Net Driver (pid=%d): HonggfuzzNetDriver_main() function exited with: %d",
52         (int)getpid(), ret);
53     _exit(ret);
54 }
55 
netDriver_startOriginalProgramInThread(void)56 static void netDriver_startOriginalProgramInThread(void) {
57     pthread_t t;
58     pthread_attr_t attr;
59 
60     pthread_attr_init(&attr);
61     pthread_attr_setstacksize(&attr, 1024ULL * 1024ULL * 8ULL);
62     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
63 
64     if (pthread_create(&t, &attr, netDriver_mainProgram, NULL) != 0) {
65         PLOG_F("Couldn't create the 'netDriver_mainProgram' thread");
66     }
67 }
68 
netDriver_initNsIfNeeded(void)69 static void netDriver_initNsIfNeeded(void) {
70     static bool initialized = false;
71     if (initialized) {
72         return;
73     }
74     initialized = true;
75 
76 #if defined(_HF_ARCH_LINUX)
77     if (!nsEnter(CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS)) {
78         LOG_F("nsEnter(CLONE_NEWUSER|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWIPC|CLONE_NEWUTS) failed");
79     }
80     if (!nsIfaceUp("lo")) {
81         LOG_F("nsIfaceUp('lo') failed");
82     }
83     if (mkdir(HFND_TMP_DIR_OLD, 0755) == -1 && errno != EEXIST) {
84         PLOG_F("mkdir('%s', 0755)", HFND_TMP_DIR_OLD);
85     }
86     if (mkdir(HFND_TMP_DIR, 0755) == -1 && errno != EEXIST) {
87         PLOG_F("mkdir('%s', 0755)", HFND_TMP_DIR);
88     }
89     if (!nsMountTmpfs(HFND_TMP_DIR_OLD)) {
90         LOG_F("nsMountTmpfs('%s') failed", HFND_TMP_DIR_OLD);
91     }
92     if (!nsMountTmpfs(HFND_TMP_DIR)) {
93         LOG_F("nsMountTmpfs('%s') failed", HFND_TMP_DIR);
94     }
95     return;
96 #endif /* defined(_HF_ARCH_LINUX) */
97     LOG_W("Honggfuzz Net Driver (pid=%d): Namespaces not enabled for this OS platform",
98         (int)getpid());
99 }
100 
101 /*
102  * Try to bind the client socket to a random loopback address, to avoid problems with exhausted
103  * ephemeral ports. We run out of them, because the TIME_WAIT state is imposed on recently closed
104  * TCP connections originating from the same IP address (127.0.0.1), and connecting to the singular
105  * IP address (again, 127.0.0.1) on a single port
106  */
netDriver_bindToRndLoopback(int sock,sa_family_t sa_family)107 static void netDriver_bindToRndLoopback(int sock, sa_family_t sa_family) {
108     if (sa_family != AF_INET) {
109         return;
110     }
111     const struct sockaddr_in bsaddr = {
112         .sin_family = AF_INET,
113         .sin_port = htons(0),
114         .sin_addr.s_addr = htonl((((uint32_t)util_rnd64()) & 0x00FFFFFF) | 0x7F000000),
115     };
116     if (bind(sock, (struct sockaddr *)&bsaddr, sizeof(bsaddr)) == -1) {
117         PLOG_W("Could not bind to a random IPv4 Loopback address");
118     }
119 }
120 
netDriver_sockConnAddr(const struct sockaddr * addr,socklen_t socklen)121 static int netDriver_sockConnAddr(const struct sockaddr *addr, socklen_t socklen) {
122     int sock = socket(addr->sa_family, SOCK_STREAM, 0);
123     if (sock == -1) {
124         PLOG_D("socket(type=%d for dst_addr='%s', SOCK_STREAM, 0)", addr->sa_family,
125             files_sockAddrToStr(addr));
126         return -1;
127     }
128     int val = 1;
129     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) == -1) {
130         PLOG_W("setsockopt(sock=%d, SOL_SOCKET, SO_REUSEADDR, %d)", sock, val);
131     }
132 #if defined(SOL_TCP) && defined(TCP_NODELAY)
133     val = 1;
134     if (setsockopt(sock, SOL_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) == -1) {
135         PLOG_W("setsockopt(sock=%d, SOL_TCP, TCP_NODELAY, %d)", sock, val);
136     }
137 #endif                         /* defined(SOL_TCP) && defined(TCP_NODELAY) */
138     val = (1024ULL * 1024ULL); /* 1MiB */
139     if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, (socklen_t)sizeof(val)) == -1) {
140         PLOG_D("setsockopt(sock=%d, SOL_SOCKET, SO_SNDBUF, %d)", sock, val);
141     }
142 
143     netDriver_bindToRndLoopback(sock, addr->sa_family);
144 
145     LOG_D("Connecting to '%s'", files_sockAddrToStr(addr));
146     if (TEMP_FAILURE_RETRY(connect(sock, addr, socklen)) == -1) {
147         PLOG_W("connect(addr='%s')", files_sockAddrToStr(addr));
148         close(sock);
149         return -1;
150     }
151     return sock;
152 }
153 
netDriver_sockConnLoopback(sa_family_t sa_family,uint16_t portno)154 int netDriver_sockConnLoopback(sa_family_t sa_family, uint16_t portno) {
155     if (portno < 1) {
156         LOG_F("Specified TCP port (%d) cannot be < 1", portno);
157     }
158 
159     if (sa_family == AF_INET) {
160         /* IPv4's 127.0.0.1 */
161         const struct sockaddr_in saddr4 = {
162             .sin_family = AF_INET,
163             .sin_port = htons(portno),
164             .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
165         };
166         return netDriver_sockConnAddr((const struct sockaddr *)&saddr4, sizeof(saddr4));
167     }
168 
169     if (sa_family == AF_INET6) {
170         /* IPv6's ::1 */
171         const struct sockaddr_in6 saddr6 = {
172             .sin6_family = AF_INET6,
173             .sin6_port = htons(portno),
174             .sin6_flowinfo = 0,
175             .sin6_addr = in6addr_loopback,
176             .sin6_scope_id = 0,
177         };
178         return netDriver_sockConnAddr((const struct sockaddr *)&saddr6, sizeof(saddr6));
179     }
180 
181     LOG_E("Unknown SA_FAMILY=%d specified", (int)sa_family);
182     return -1;
183 }
184 
185 /*
186  * Decide which TCP port should be used for sending inputs
187  */
HonggfuzzNetDriverPort(int argc HF_ATTR_UNUSED,char ** argv HF_ATTR_UNUSED)188 __attribute__((weak)) uint16_t HonggfuzzNetDriverPort(
189     int argc HF_ATTR_UNUSED, char **argv HF_ATTR_UNUSED) {
190     /* Return the default port (8080) */
191     return 8080;
192 }
193 
194 /*
195  * The return value is a number of arguments passed returned to libfuzzer (if used)
196  *
197  * Define this function in your code to describe which arguments are passed to the fuzzed
198  * TCP server, and which to the fuzzing engine.
199  */
HonggfuzzNetDriverArgsForServer(int argc,char ** argv,int * server_argc,char *** server_argv)200 __attribute__((weak)) int HonggfuzzNetDriverArgsForServer(
201     int argc, char **argv, int *server_argc, char ***server_argv) {
202     /* If the used fuzzer is honggfuzz, simply pass all arguments to the TCP server */
203     __attribute__((weak)) int HonggfuzzMain(int argc, char **argv);
204     if (HonggfuzzMain) {
205         *server_argc = argc;
206         *server_argv = argv;
207         return argc;
208     }
209 
210     /*
211      * For other fuzzing engines:
212      * Split: ./httpdserver -max_input=10 -- --config /etc/httpd.confg
213      * into:
214      * The fuzzing engine (e.g. libfuzzer) will see "./httpdserver -max_input=10",
215      * The httpdserver will see: "./httpdserver --config /etc/httpd.confg"
216      */
217     for (int i = 0; i < argc; i++) {
218         if (strcmp(argv[i], "--") == 0) {
219             /* Replace '--' with argv[0] */
220             argv[i] = argv[0];
221             *server_argc = argc - i;
222             *server_argv = &argv[i];
223             return i;
224         }
225     }
226 
227     LOG_I("Honggfuzz Net Driver (pid=%d): No '--' was found in the commandline, and therefore no "
228           "arguments will be passed to the TCP server program",
229         (int)getpid());
230     *server_argc = 1;
231     *server_argv = &argv[0];
232     return argc;
233 }
234 
netDriver_waitForServerReady(uint16_t portno)235 static void netDriver_waitForServerReady(uint16_t portno) {
236     for (;;) {
237         int fd = -1;
238         fd = netDriver_sockConnLoopback(AF_INET, portno);
239         if (fd >= 0) {
240             hfnd_globals.sa_family = AF_INET;
241             close(fd);
242             return;
243         }
244         fd = netDriver_sockConnLoopback(AF_INET6, portno);
245         if (fd >= 0) {
246             hfnd_globals.sa_family = AF_INET6;
247             close(fd);
248             return;
249         }
250         LOG_I(
251             "Honggfuzz Net Driver (pid=%d): Waiting for the TCP server process to start accepting "
252             "connections at TCP4:127.0.0.1:%" PRIu16 " or at TCP6:[::1]:%" PRIu16
253             ". Sleeping for 0.5 seconds ...",
254             (int)getpid(), portno, portno);
255 
256         util_sleepForMSec(500);
257     }
258 }
259 
netDriver_getTCPPort(int argc,char ** argv)260 uint16_t netDriver_getTCPPort(int argc, char **argv) {
261     const char *port_str = getenv(HFND_TCP_PORT_ENV);
262     if (port_str) {
263         errno = 0;
264         signed long portsl = strtol(port_str, NULL, 0);
265         if (errno != 0) {
266             PLOG_F("Couldn't convert '%s'='%s' to a number", HFND_TCP_PORT_ENV, port_str);
267         }
268         if (portsl < 1) {
269             LOG_F("Specified TCP port '%s'='%s' (%ld) cannot be < 1", HFND_TCP_PORT_ENV, port_str,
270                 portsl);
271         }
272         if (portsl > 65535) {
273             LOG_F("Specified TCP port '%s'='%s' (%ld) cannot be > 65535", HFND_TCP_PORT_ENV,
274                 port_str, portsl);
275         }
276         return (uint16_t)portsl;
277     }
278 
279     return HonggfuzzNetDriverPort(argc, argv);
280 }
281 
LLVMFuzzerInitialize(int * argc,char *** argv)282 __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv) {
283     if (getenv(HFND_SKIP_FUZZING_ENV)) {
284         LOG_I(
285             "Honggfuzz Net Driver (pid=%d): '%s' is set, skipping fuzzing, calling main() directly",
286             getpid(), HFND_SKIP_FUZZING_ENV);
287         exit(HonggfuzzNetDriver_main(*argc, *argv));
288     }
289 
290     /* Make sure LIBHFNETDRIVER_module_netdriver (NetDriver signature) is used */
291     LOG_D("Module: %s", LIBHFNETDRIVER_module_netdriver);
292 
293     hfnd_globals.tcp_port = netDriver_getTCPPort(*argc, *argv);
294     *argc = HonggfuzzNetDriverArgsForServer(
295         *argc, *argv, &hfnd_globals.argc_server, &hfnd_globals.argv_server);
296 
297     LOG_I(
298         "Honggfuzz Net Driver (pid=%d): TCP port %d will be used. You can change the server's TCP "
299         "port by setting the %s envvar",
300         (int)getpid(), hfnd_globals.tcp_port, HFND_TCP_PORT_ENV);
301 
302     netDriver_initNsIfNeeded();
303     netDriver_startOriginalProgramInThread();
304     netDriver_waitForServerReady(hfnd_globals.tcp_port);
305 
306     LOG_I("Honggfuzz Net Driver (pid=%d): The TCP server process is ready to accept connections at "
307           "%s:%" PRIu16 ". TCP fuzzing starts now!",
308         (int)getpid(), (hfnd_globals.sa_family == AF_INET ? "TCP4:127.0.0.1" : "TCP6:[::1]"),
309         hfnd_globals.tcp_port);
310 
311     return 0;
312 }
313 
LLVMFuzzerTestOneInput(const uint8_t * buf,size_t len)314 __attribute__((weak)) int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) {
315     int sock = netDriver_sockConnLoopback(hfnd_globals.sa_family, hfnd_globals.tcp_port);
316     if (sock == -1) {
317         LOG_F("Couldn't connect to the server TCP port");
318     }
319     if (!files_sendToSocket(sock, buf, len)) {
320         PLOG_W("files_sendToSocket(sock=%d, len=%zu) failed", sock, len);
321         close(sock);
322         return 0;
323     }
324     /*
325      * Indicate EOF (via the FIN flag) to the TCP server
326      *
327      * Well-behaved TCP servers should process the input and respond/close the TCP connection at
328      * this point
329      */
330     if (TEMP_FAILURE_RETRY(shutdown(sock, SHUT_WR)) == -1) {
331         if (errno == ENOTCONN) {
332             close(sock);
333             return 0;
334         }
335         PLOG_F("shutdown(sock=%d, SHUT_WR)", sock);
336     }
337 
338     /*
339      * Try to read data from the server, assuming that an early TCP close would sometimes cause the
340      * TCP server to drop the input data, instead of processing it. Use BSS to avoid putting
341      * pressure on the stack size
342      */
343     static char b[1024ULL * 1024ULL * 4ULL];
344     while (TEMP_FAILURE_RETRY(recv(sock, b, sizeof(b), MSG_WAITALL)) > 0)
345         ;
346 
347     close(sock);
348 
349     return 0;
350 }
351