Home | History | Annotate | Download | only in libhfnetdriver
      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