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 __attribute__((weak)) int HonggfuzzNetDriver_main( 48 int argc HF_ATTR_UNUSED, char **argv HF_ATTR_UNUSED) { 49 LOG_F("The HonggfuzzNetDriver_main function was not defined in your code"); 50 return EXIT_FAILURE; 51 } 52 53 static void *netDriver_mainProgram(void *unused HF_ATTR_UNUSED) { 54 int ret = HonggfuzzNetDriver_main(hfnd_globals.argc_server, hfnd_globals.argv_server); 55 LOG_I("Honggfuzz Net Driver (pid=%d): HonggfuzzNetDriver_main() function exited with: %d", 56 (int)getpid(), ret); 57 _exit(ret); 58 } 59 60 static void netDriver_startOriginalProgramInThread(void) { 61 pthread_t t; 62 pthread_attr_t attr; 63 64 pthread_attr_init(&attr); 65 pthread_attr_setstacksize(&attr, 1024ULL * 1024ULL * 8ULL); 66 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 67 68 if (pthread_create(&t, &attr, netDriver_mainProgram, NULL) != 0) { 69 PLOG_F("Couldn't create the 'netDriver_mainProgram' thread"); 70 } 71 } 72 73 static void netDriver_initNsIfNeeded(void) { 74 static bool initialized = false; 75 if (initialized) { 76 return; 77 } 78 initialized = true; 79 80 #if defined(_HF_ARCH_LINUX) 81 if (!nsEnter(CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS)) { 82 LOG_F("nsEnter(CLONE_NEWUSER|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWIPC|CLONE_NEWUTS) failed"); 83 } 84 if (!nsIfaceUp("lo")) { 85 LOG_F("nsIfaceUp('lo') failed"); 86 } 87 if (mkdir(HFND_TMP_DIR_OLD, 0755) == -1 && errno != EEXIST) { 88 PLOG_F("mkdir('%s', 0755)", HFND_TMP_DIR_OLD); 89 } 90 if (mkdir(HFND_TMP_DIR, 0755) == -1 && errno != EEXIST) { 91 PLOG_F("mkdir('%s', 0755)", HFND_TMP_DIR); 92 } 93 if (!nsMountTmpfs(HFND_TMP_DIR_OLD)) { 94 LOG_F("nsMountTmpfs('%s') failed", HFND_TMP_DIR_OLD); 95 } 96 if (!nsMountTmpfs(HFND_TMP_DIR)) { 97 LOG_F("nsMountTmpfs('%s') failed", HFND_TMP_DIR); 98 } 99 return; 100 #endif /* defined(_HF_ARCH_LINUX) */ 101 LOG_W("Honggfuzz Net Driver (pid=%d): Namespaces not enabled for this OS platform", 102 (int)getpid()); 103 } 104 105 /* 106 * Try to bind the client socket to a random loopback address, to avoid problems with exhausted 107 * ephemeral ports. We run out of them, because the TIME_WAIT state is imposed on recently closed 108 * TCP connections originating from the same IP address (127.0.0.1), and connecting to the singular 109 * IP address (again, 127.0.0.1) on a single port 110 */ 111 static void netDriver_bindToRndLoopback(int sock, sa_family_t sa_family) { 112 if (sa_family != AF_INET) { 113 return; 114 } 115 const struct sockaddr_in bsaddr = { 116 .sin_family = AF_INET, 117 .sin_port = htons(0), 118 .sin_addr.s_addr = htonl((((uint32_t)util_rnd64()) & 0x00FFFFFF) | 0x7F000000), 119 }; 120 if (bind(sock, (struct sockaddr *)&bsaddr, sizeof(bsaddr)) == -1) { 121 PLOG_W("Could not bind to a random IPv4 Loopback address"); 122 } 123 } 124 125 static int netDriver_sockConnAddr(const struct sockaddr *addr, socklen_t socklen) { 126 int sock = socket(addr->sa_family, SOCK_STREAM, 0); 127 if (sock == -1) { 128 PLOG_D("socket(type=%d for dst_addr='%s', SOCK_STREAM, 0)", addr->sa_family, 129 files_sockAddrToStr(addr)); 130 return -1; 131 } 132 int val = 1; 133 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) == -1) { 134 PLOG_W("setsockopt(sock=%d, SOL_SOCKET, SO_REUSEADDR, 1)", sock); 135 } 136 #if defined(SOL_TCP) && defined(TCP_NODELAY) 137 val = 1; 138 if (setsockopt(sock, SOL_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) == -1) { 139 PLOG_W("setsockopt(sock=%d, SOL_TCP, TCP_NODELAY, 1)", sock); 140 } 141 #endif /* defined(SOL_TCP) && defined(TCP_NODELAY) */ 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 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 */ 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 */ 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 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 usleep(500000U); 256 } 257 } 258 259 uint16_t netDriver_getTCPPort(int argc, char **argv) { 260 const char *port_str = getenv(HFND_TCP_PORT_ENV); 261 if (port_str) { 262 errno = 0; 263 signed long portsl = strtol(port_str, NULL, 0); 264 if (errno != 0) { 265 PLOG_F("Couldn't convert '%s'='%s' to a number", HFND_TCP_PORT_ENV, port_str); 266 } 267 if (portsl < 1) { 268 LOG_F("Specified TCP port '%s'='%s' (%ld) cannot be < 1", HFND_TCP_PORT_ENV, port_str, 269 portsl); 270 } 271 if (portsl > 65535) { 272 LOG_F("Specified TCP port '%s'='%s' (%ld) cannot be > 65535", HFND_TCP_PORT_ENV, 273 port_str, portsl); 274 } 275 return (uint16_t)portsl; 276 } 277 278 return HonggfuzzNetDriverPort(argc, argv); 279 } 280 281 __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv) { 282 if (getenv(HFND_SKIP_FUZZING_ENV)) { 283 LOG_I( 284 "Honggfuzz Net Driver (pid=%d): '%s' is set, skipping fuzzing, calling main() directly", 285 getpid(), HFND_SKIP_FUZZING_ENV); 286 if (!HonggfuzzNetDriver_main) { 287 LOG_F("Honggfuzz Net Driver (pid=%d): HonggfuzzNetDriver_main was not defined in your " 288 "code", 289 getpid()); 290 } 291 exit(HonggfuzzNetDriver_main(*argc, *argv)); 292 } 293 294 /* Make sure LIBHFNETDRIVER_module_netdriver (NetDriver signature) is used */ 295 LOG_D("Module: %s", LIBHFNETDRIVER_module_netdriver); 296 297 hfnd_globals.tcp_port = netDriver_getTCPPort(*argc, *argv); 298 *argc = HonggfuzzNetDriverArgsForServer( 299 *argc, *argv, &hfnd_globals.argc_server, &hfnd_globals.argv_server); 300 301 LOG_I( 302 "Honggfuzz Net Driver (pid=%d): TCP port %d will be used. You can change the server's TCP " 303 "port by setting the %s envvar", 304 (int)getpid(), hfnd_globals.tcp_port, HFND_TCP_PORT_ENV); 305 306 netDriver_initNsIfNeeded(); 307 netDriver_startOriginalProgramInThread(); 308 netDriver_waitForServerReady(hfnd_globals.tcp_port); 309 310 LOG_I("Honggfuzz Net Driver (pid=%d): The TCP server process is ready to accept connections at " 311 "%s:%" PRIu16 ". TCP fuzzing starts now!", 312 (int)getpid(), (hfnd_globals.sa_family == AF_INET ? "TCP4:127.0.0.1" : "TCP6:[::1]"), 313 hfnd_globals.tcp_port); 314 315 return 0; 316 } 317 318 __attribute__((weak)) int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { 319 int sock = netDriver_sockConnLoopback(hfnd_globals.sa_family, hfnd_globals.tcp_port); 320 if (sock == -1) { 321 LOG_F("Couldn't connect to the server TCP port"); 322 } 323 if (!files_sendToSocket(sock, buf, len)) { 324 PLOG_E("files_sendToSocket(sock=%d, len=%zu) failed", sock, len); 325 close(sock); 326 return 0; 327 } 328 /* 329 * Indicate EOF (via the FIN flag) to the TCP server 330 * 331 * Well-behaved TCP servers should process the input and respond/close the TCP connection at 332 * this point 333 */ 334 if (TEMP_FAILURE_RETRY(shutdown(sock, SHUT_WR)) == -1) { 335 if (errno == ENOTCONN) { 336 close(sock); 337 return 0; 338 } 339 PLOG_F("shutdown(sock=%d, SHUT_WR)", sock); 340 } 341 342 /* 343 * Try to read data from the server, assuming that an early TCP close would sometimes cause the 344 * TCP server to drop the input data, instead of processing it. Use BSS to avoid putting 345 * pressure on the stack size 346 */ 347 static char b[1024ULL * 1024ULL * 4ULL]; 348 while (TEMP_FAILURE_RETRY(recv(sock, b, sizeof(b), MSG_WAITALL)) > 0) 349 ; 350 351 close(sock); 352 353 return 0; 354 } 355