diff --git a/test/fuzz/Makefile b/test/fuzz/Makefile index ccbe9561..150b2a01 100644 --- a/test/fuzz/Makefile +++ b/test/fuzz/Makefile @@ -29,7 +29,7 @@ # Author: Adam Dunkels # -all compile: lwip_fuzz +all compile: lwip_fuzz triple_fuzz .PHONY: all clean CC=afl-gcc @@ -41,14 +41,17 @@ CONTRIBDIR=../../../lwip-contrib include $(CONTRIBDIR)/ports/unix/Common.mk clean: - rm -f *.o $(LWIPLIBCOMMON) $(APPLIB) lwip_fuzz *.s .depend* *.core core + rm -f *.o $(LWIPLIBCOMMON) $(APPLIB) triple_fuzz lwip_fuzz *.s .depend* *.core core depend dep: .depend include .depend -.depend: fuzz.c $(LWIPFILES) $(APPFILES) +.depend: triple_driver.c fuzz.c $(LWIPFILES) $(APPFILES) $(CCDEP) $(CFLAGS) -MM $^ > .depend || rm -f .depend lwip_fuzz: .depend $(LWIPLIBCOMMON) $(APPLIB) fuzz.o $(CC) $(CFLAGS) -o lwip_fuzz fuzz.o $(APPLIB) $(LWIPLIBCOMMON) $(LDFLAGS) + +triple_fuzz: .depend $(LWIPLIBCOMMON) $(APPLIB) triple_driver.o + $(CC) $(CFLAGS) -o triple_fuzz triple_driver.o $(APPLIB) $(LWIPLIBCOMMON) $(LDFLAGS) diff --git a/test/fuzz/lwipopts.h b/test/fuzz/lwipopts.h index 4ab26f28..4022ccca 100644 --- a/test/fuzz/lwipopts.h +++ b/test/fuzz/lwipopts.h @@ -40,7 +40,11 @@ #define LWIP_IPV6 1 #define IPV6_FRAG_COPYHEADER 1 +#ifndef FUZZED_TMR #define LWIP_IPV6_DUP_DETECT_ATTEMPTS 0 +#else +#define LWIP_IPv6_DUP_DETECT_ATTEMPTS 1 +#endif /* Enable some protocols to test them */ #define LWIP_DHCP 1 diff --git a/test/fuzz/triple_driver.c b/test/fuzz/triple_driver.c new file mode 100644 index 00000000..270235e8 --- /dev/null +++ b/test/fuzz/triple_driver.c @@ -0,0 +1,527 @@ +#include "lwip/init.h" +#include "lwip/netif.h" +#include "lwip/altcp_tcp.h" +#include "lwip/udp.h" +#include "lwip/timeouts.h" +#if LWIP_IPV6 +#include "lwip/ethip6.h" +#include "lwip/nd6.h" +#endif /* LWIP_IPV6 */ +#include "netif/ethernet.h" +#include "netif/etharp.h" +#include "lwip/tcpbase.h" + +#include +#include + +/** + * lwip_tx_func + * Dummy TX function. + */ +static err_t +lwip_tx_func(struct netif *netif, struct pbuf *p) +{ + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(p); + + return ERR_OK; +} + +/** + * testif_init + * Initialize a netif for fuzzing. + */ +static err_t +testif_init(struct netif *netif) +{ + netif->name[0] = 'f'; + netif->name[1] = 'z'; + netif->output = etharp_output; + netif->linkoutput = lwip_tx_func; + netif->mtu = 1500; + netif->hwaddr_len = 6; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + netif->hwaddr[0] = 0x00; + netif->hwaddr[1] = 0x23; + netif->hwaddr[2] = 0xC1; + netif->hwaddr[3] = 0xDE; + netif->hwaddr[4] = 0xD0; + netif->hwaddr[5] = 0x0D; + +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; + netif->ip6_autoconfig_enabled = 1; + netif_create_ip6_linklocal_address(netif, 1); + netif->flags |= NETIF_FLAG_MLD6; +#endif /* LWIP_IPV6 */ + + return ERR_OK; +} + +static const u8_t *remfuzz_ptr; /* remaining fuzz pointer */ +static size_t remfuzz_len; /* remaining fuzz length */ + +/** + * tcp_app_fuzz_input + * Input fuzz with a write function for TCP. + */ +static void +tcp_app_fuzz_input(struct altcp_pcb *pcb) +{ + if (remfuzz_len > sizeof(u16_t)) { + /* + * (max IP packet size) - ((minimum IP header size) + (minimum TCP header size)) + * = 65535 - (20 + 20) + * = 65495 + */ + const u16_t max_data_size = 65495; + u16_t data_len; + + memcpy(&data_len, remfuzz_ptr, sizeof(u16_t)); + remfuzz_ptr += sizeof(u16_t); + remfuzz_len -= sizeof(u16_t); + data_len = htons(data_len); + data_len = LWIP_MIN(data_len, max_data_size); + if (data_len > remfuzz_len) { + data_len = (u16_t)remfuzz_len; + } + + if (data_len != 0) { + altcp_write(pcb, remfuzz_ptr, data_len, TCP_WRITE_FLAG_COPY); + altcp_output(pcb); + } else { + altcp_close(pcb); + } + + remfuzz_ptr += data_len; + remfuzz_len -= data_len; + } +} + +/** + * udp_app_fuzz_input + * Input fuzz with write functions for UDP. + */ +static void +udp_app_fuzz_input(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port) +{ + if (remfuzz_len > sizeof(u16_t)) { + /* + * (max IP packet size) - ((minimum IP header size) - (minimum UDP header size)) + * = 65535 - (20 + 8) + * = 65507 + */ + const u16_t max_data_size = 65507; + u16_t data_len; + + memcpy(&data_len, remfuzz_ptr, sizeof(u16_t)); + remfuzz_ptr += sizeof(u16_t); + remfuzz_len -= sizeof(u16_t); + data_len = htons(data_len); + data_len = LWIP_MIN(data_len, max_data_size); + if (data_len > remfuzz_len) { + data_len = (u16_t)remfuzz_len; + } + + if (data_len != 0) { + struct pbuf *p, *q; + + p = pbuf_alloc(PBUF_RAW, (u16_t)data_len, PBUF_POOL); + LWIP_ASSERT("alloc failed", p); + + for (q = p; q != NULL; q = q->next) { + MEMCPY(q->payload, remfuzz_ptr, q->len); + remfuzz_ptr += q->len; + } + remfuzz_len -= data_len; + + /* + * Trying input from ... + * + * client: + * The pcb has information about the destination. + * We use udp_send(). + * + * server: + * The pcb does NOT have infomation about the destionation. + * We use udp_sendto(). + */ + if (addr == NULL) { + udp_send(pcb, p); + } else { + udp_sendto(pcb, p, addr, port); + } + pbuf_free(p); + } + } +} + +/** + * tcp_client_connected + * A connected callback function (for the TCP client) + */ +static err_t +tcp_client_connected(void *arg, struct altcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + tcp_app_fuzz_input(pcb); + + return ERR_OK; +} + +/** + * tcp_client_recv + * A recv callback function (for the TCP client) + */ +static err_t +tcp_client_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + if (p == NULL) { + altcp_close(pcb); + } else { + altcp_recved(pcb, p->tot_len); + tcp_app_fuzz_input(pcb); + pbuf_free(p); + } + + return ERR_OK; +} + +/** + * tcp_client_sent + * A sent callback function (for the TCP client) + */ +static err_t +tcp_client_sent(void *arg, struct altcp_pcb *pcb, u16_t len) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(len); + return ERR_OK; +} + +/** + * tcp_client_poll + * A poll callback function (for the TCP client) + */ +static err_t +tcp_client_poll(void *arg, struct altcp_pcb *pcb) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + return ERR_OK; +} + +/** + * tcp_client_err + * An err callback function (for the TCP client) + */ +static void +tcp_client_err(void *arg, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); +} + +/** + * tcp_server_recv + * A recv callback function (for the TCP server) + */ +static err_t +tcp_server_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + if (p == NULL) { + altcp_close(pcb); + } else { + altcp_recved(pcb, p->tot_len); + tcp_app_fuzz_input(pcb); + pbuf_free(p); + } + + return ERR_OK; +} + +/** + * tcp_server_sent + * A sent callback function (for the TCP server) + */ +static err_t +tcp_server_sent(void *arg, struct altcp_pcb *pcb, u16_t len) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(len); + return ERR_OK; +} + +/** + * tcp_server_poll + * A poll callback function (for the TCP server) + */ +static err_t +tcp_server_poll(void *arg, struct altcp_pcb *pcb) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + return ERR_OK; +} + +/** + * tcp_server_err + * An err callbuck function (for the TCP server) + */ +static void +tcp_server_err(void *arg, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); +} + +/** + * tcp_server_accept + * An accept callbuck function (for the TCP server) + */ +static err_t +tcp_server_accept(void *arg, struct altcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + if ((err != ERR_OK) || (pcb == NULL)) { + return ERR_VAL; + } + + altcp_setprio(pcb, TCP_PRIO_MIN); + + altcp_recv(pcb, tcp_server_recv); + altcp_err(pcb, tcp_server_err); + altcp_poll(pcb, tcp_server_poll, 10); + altcp_sent(pcb, tcp_server_sent); + + return ERR_OK; +} + +/** + * udp_client_recv + * A recv callback function (for the UDP client) + */ +static void +udp_client_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + if (p == NULL) { + udp_disconnect(pcb); + } else { + /* + * We call the function with 2nd argument set to NULL + * to input fuzz from udp_send. + */ + udp_app_fuzz_input(pcb, NULL, port); + pbuf_free(p); + } +} + +/** + * udp_server_recv + * A recv callback functyion (for the UDP server) + */ +static void +udp_server_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + if (p != NULL) { + udp_app_fuzz_input(pcb, addr, port); + pbuf_free(p); + } +} + +u32_t total_external_delay; /* total of external delay time. */ + + +/** + * main + * The entry point function of the test driver. + */ +int +main(int argc, char **argv) +{ + + struct netif test_netif; /* a netif for fuzzing */ + ip4_addr_t netif_addr; /* an IPv4 addr for test_netif */ + ip4_addr_t netif_netmask; /* a netmask for test_netif */ + ip4_addr_t netif_gw; /* a default-gateway for test_netif */ + + struct udp_pcb *udp_client_pcb; /* a pcb for the UDP client */ + struct udp_pcb *udp_server_pcb; /* a pcb for the UDP server */ + u16_t udp_remote_port; /* a UDP port number of the destination */ + u16_t udp_local_port; /* a UDP port number of the local server*/ + + struct altcp_pcb *tcp_client_pcb; /* a pcb for the TCP client */ + struct altcp_pcb *tcp_server_pcb; /* a pcb for the TCP server */ + u16_t tcp_remote_port; /* a TCP port number of the destionation */ + u16_t tcp_local_port; /* a TCP port number of the local server */ + + ip_addr_t remote_addr; /* a IPv4 addr of the destination */ + struct eth_addr remote_mac = ETH_ADDR(0x38, 0x00, 0x00, 0x22, 0x2b, 0x38); /* a MAC addr of the destination */ + + err_t err; + size_t fuzz_len; /* fuzz length */ + u8_t fuzz_buf[20000]; /* fuzz buffer */ + + + /* Initialize lwIP */ + lwip_init(); + + /* netif setup */ + ip4addr_aton("172.30.115.84", &netif_addr); + ip4addr_aton("255.255.255.0", &netif_netmask); + ip4addr_aton("172.30.115.1", &netif_gw); + netif_add(&test_netif, &netif_addr, &netif_netmask, &netif_gw, + &test_netif, testif_init, ethernet_input); + netif_set_up(&test_netif); + netif_set_link_up(&test_netif); + + /* Add the ARP entry */ + ipaddr_aton("172.30.115.37", &remote_addr); + etharp_add_static_entry(&(remote_addr.u_addr.ip4), &remote_mac); + + /* Call nd6_tmr() to activate neightbor discovery */ + nd6_tmr(); + + /* + * Initialize dummy applications + */ + + /* TCP client */ + tcp_client_pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("Error: altcp_new() failed", tcp_client_pcb != NULL); + tcp_remote_port = 80; + err = altcp_connect(tcp_client_pcb, &remote_addr, tcp_remote_port, tcp_client_connected); + LWIP_ASSERT("Error: altcp_connect() failed", err == ERR_OK); + altcp_recv(tcp_client_pcb, tcp_client_recv); + altcp_err(tcp_client_pcb, tcp_client_err); + altcp_poll(tcp_client_pcb, tcp_client_poll, 10); + altcp_sent(tcp_client_pcb, tcp_client_sent); + /* TCP server */ + tcp_server_pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("Error: altcp_new() failed", tcp_server_pcb != NULL); + altcp_setprio(tcp_server_pcb, TCP_PRIO_MIN); + tcp_local_port = 80; + err = altcp_bind(tcp_server_pcb, IP_ANY_TYPE, tcp_local_port); + LWIP_ASSERT("Error: altcp_bind() failed", err == ERR_OK); + tcp_server_pcb = altcp_listen(tcp_server_pcb); + LWIP_ASSERT("Error: altcp_listen() failed", err == ERR_OK); + altcp_accept(tcp_server_pcb, tcp_server_accept); + /* UDP client */ + udp_client_pcb = udp_new(); + udp_new_ip_type(IPADDR_TYPE_ANY); + udp_recv(udp_client_pcb, udp_client_recv, NULL); + udp_remote_port = 161; + udp_connect(udp_client_pcb, &remote_addr, udp_remote_port); + /* UDP server */ + udp_server_pcb = udp_new(); + udp_new_ip_type(IPADDR_TYPE_ANY); + udp_local_port = 161; + udp_bind(udp_server_pcb, IP_ANY_TYPE, udp_local_port); + udp_recv(udp_server_pcb, udp_server_recv, NULL); + + /* Read fuzz */ + switch (argc) { + case 1: { + fuzz_len = fread(fuzz_buf, 1, sizeof(fuzz_buf), stdin); + break; + } + case 2: { + FILE *fp; + const char *fname; + + printf("Reading input from file... "); + fflush(stdout); + + fname = argv[argc - 1]; + LWIP_ASSERT("Invalid file name", fname != NULL); + fp = fopen(fname, "rb"); + LWIP_ASSERT("Open failed", fp != NULL); + fuzz_len = fread(fuzz_buf, 1, sizeof(fuzz_buf), fp); + fclose(fp); + printf("testing file: \"%s\"...\n", fname); + break; + } + default: { + printf("Error: You can use just one argument or less.\n"); + exit(1); + break; + } + } + + remfuzz_ptr = fuzz_buf; + remfuzz_len = fuzz_len; + + /* Main loop */ + while (remfuzz_len > (sizeof(u32_t) + sizeof(u16_t))) { + const u16_t max_frame_len = 1514; + u16_t frame_len; + u32_t external_delay; + + /* Extract external delay time from fuzz pool */ + memcpy(&external_delay, remfuzz_ptr, sizeof(u32_t)); + remfuzz_ptr += sizeof(u32_t); + remfuzz_len -= sizeof(u32_t); + external_delay = htonl(external_delay); + + /* Extract length of a ethernet frame from fuzz pool */ + memcpy(&frame_len, remfuzz_ptr, sizeof(u16_t)); + remfuzz_ptr += sizeof(u16_t); + remfuzz_len -= sizeof(u16_t); + frame_len = htons(frame_len) & 0x7FF; + frame_len = LWIP_MIN(frame_len, max_frame_len); + if (frame_len > remfuzz_len) { + frame_len = (u16_t)remfuzz_len; + } + + /* Extract a ethernet frame from fuzz pool, and input it to lwIP */ + if (frame_len != 0) { + struct pbuf *p, *q; + + /* Update total external delay time, and check timeouts */ + total_external_delay += external_delay; + sys_check_timeouts(); + + p = pbuf_alloc(PBUF_RAW, (u16_t)frame_len, PBUF_POOL); + LWIP_ASSERT("alloc failed", p); + + for (q = p; q != NULL; q = q->next) { + MEMCPY(q->payload, remfuzz_ptr, q->len); + remfuzz_ptr += q->len; + } + remfuzz_len -= frame_len; + + err = test_netif.input(p, &test_netif); + if (err != ERR_OK) { + pbuf_free(p); + } + } + + /* Check timeouts again */ + sys_check_timeouts(); + } + + return 0; +}