| #include "helper.h" | |
| #include <glib.h> | |
| #include <stdlib.h> | |
| #include "../src/libslirp.h" | |
| #include "../src/ip6.h" | |
| #include "slirp_base_fuzz.h" | |
| #define MIN_NUMBER_OF_RUNS 1 | |
| #define EXIT_TEST_SKIP 77 | |
| int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); | |
| struct in6_addr ip6_host; | |
| struct in6_addr ip6_dns; | |
| /// Function to compute the checksum of the ip header, should be compatible with | |
| /// TCP and UDP checksum calculation too. | |
| uint16_t compute_checksum(uint8_t *Data, size_t Size) | |
| { | |
| uint32_t sum = 0; | |
| uint16_t *Data_as_u16 = (uint16_t *)Data; | |
| for (size_t i = 0; i < Size / 2; i++) { | |
| uint16_t val = ntohs(*(Data_as_u16 + i)); | |
| sum += val; | |
| } | |
| if (Size % 2 == 1) | |
| sum += Data[Size - 1] << 8; | |
| uint16_t carry = sum >> 16; | |
| uint32_t sum_val = carry + (sum & 0xFFFF); | |
| uint16_t result = (sum_val >> 16) + (sum_val & 0xFFFF); | |
| return ~result; | |
| } | |
| int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) | |
| { | |
| /* FIXME: fail on some addr? */ | |
| return 0; | |
| } | |
| int listen(int sockfd, int backlog) | |
| { | |
| return 0; | |
| } | |
| int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) | |
| { | |
| /* FIXME: fail on some addr? */ | |
| return 0; | |
| } | |
| ssize_t send(int sockfd, const void *buf, size_t len, int flags) | |
| { | |
| /* FIXME: partial send? */ | |
| return len; | |
| } | |
| ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, | |
| const struct sockaddr *dest_addr, socklen_t addrlen) | |
| { | |
| /* FIXME: partial send? */ | |
| return len; | |
| } | |
| ssize_t recv(int sockfd, void *buf, size_t len, int flags) | |
| { | |
| memset(buf, 0, len); | |
| return len / 2; | |
| } | |
| ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, | |
| struct sockaddr *src_addr, socklen_t *addrlen) | |
| { | |
| memset(buf, 0, len); | |
| memset(src_addr, 0, *addrlen); | |
| return len / 2; | |
| } | |
| int setsockopt(int sockfd, int level, int optname, const void *optval, | |
| socklen_t optlen) | |
| { | |
| return 0; | |
| } | |
| #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | |
| static void empty_logging_func(const gchar *log_domain, | |
| GLogLevelFlags log_level, const gchar *message, | |
| gpointer user_data) | |
| { | |
| } | |
| #endif | |
| /* Disables logging for oss-fuzz. Must be used with each target. */ | |
| static void fuzz_set_logging_func(void) | |
| { | |
| #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | |
| g_log_set_default_handler(empty_logging_func, NULL); | |
| #endif | |
| } | |
| static ssize_t send_packet(const void *pkt, size_t pkt_len, void *opaque) | |
| { | |
| return pkt_len; | |
| } | |
| static int64_t clock_get_ns(void *opaque) | |
| { | |
| return 0; | |
| } | |
| static void *timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) | |
| { | |
| return NULL; | |
| } | |
| static void timer_mod(void *timer, int64_t expire_timer, void *opaque) | |
| { | |
| } | |
| static void timer_free(void *timer, void *opaque) | |
| { | |
| } | |
| static void guest_error(const char *msg, void *opaque) | |
| { | |
| } | |
| static void register_poll_socket(slirp_os_socket fd, void *opaque) | |
| { | |
| } | |
| static void unregister_poll_socket(slirp_os_socket fd, void *opaque) | |
| { | |
| } | |
| static void notify(void *opaque) | |
| { | |
| } | |
| static const SlirpCb slirp_cb = { | |
| .send_packet = send_packet, | |
| .guest_error = guest_error, | |
| .clock_get_ns = clock_get_ns, | |
| .timer_new = timer_new, | |
| .timer_mod = timer_mod, | |
| .timer_free = timer_free, | |
| .register_poll_socket = register_poll_socket, | |
| .unregister_poll_socket = unregister_poll_socket, | |
| .notify = notify, | |
| }; | |
| #define MAX_EVID 1024 | |
| static int fake_events[MAX_EVID]; | |
| static int add_poll_cb(slirp_os_socket fd, int events, void *opaque) | |
| { | |
| g_assert(fd < G_N_ELEMENTS(fake_events)); | |
| fake_events[fd] = events; | |
| return fd; | |
| } | |
| static int get_revents_cb(int idx, void *opaque) | |
| { | |
| return fake_events[idx] & ~(SLIRP_POLL_ERR | SLIRP_POLL_HUP); | |
| } | |
| // Fuzzing strategy is the following : | |
| // LLVMFuzzerTestOneInput : | |
| // - build a slirp instance, | |
| // - extract the packets from the pcap one by one, | |
| // - send the data to `slirp_input` | |
| // - call `slirp_pollfds_fill` and `slirp_pollfds_poll` to advance slirp | |
| // - cleanup slirp when the whole pcap has been unwrapped. | |
| int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) | |
| { | |
| Slirp *slirp = NULL; | |
| struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ | |
| struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */ | |
| struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ | |
| struct in_addr fwd = { .s_addr = htonl(0x0a000205) }; /* 10.0.2.5 */ | |
| struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ | |
| struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ | |
| struct in6_addr ip6_prefix; | |
| int ret, vprefix6_len; | |
| const pcap_hdr_t *hdr = (const void *)data; | |
| const pcaprec_hdr_t *rec = NULL; | |
| uint32_t timeout = 0; | |
| if (size < sizeof(pcap_hdr_t)) { | |
| return 0; | |
| } | |
| data += sizeof(*hdr); | |
| size -= sizeof(*hdr); | |
| if (hdr->magic_number == 0xd4c3b2a1) { | |
| g_debug("FIXME: byteswap fields"); | |
| return 0; | |
| } /* else assume native pcap file */ | |
| if (hdr->network != 1) { | |
| return 0; | |
| } | |
| setenv("SLIRP_FUZZING", "1", 0); | |
| fuzz_set_logging_func(); | |
| ret = inet_pton(AF_INET6, "fec0::", &ip6_prefix); | |
| vprefix6_len = 64; | |
| g_assert_cmpint(ret, ==, 1); | |
| ip6_host = ip6_prefix; | |
| ip6_host.s6_addr[15] |= 2; | |
| ip6_dns = ip6_prefix; | |
| ip6_dns.s6_addr[15] |= 3; | |
| SlirpConfig cfg = { | |
| .version = 6, | |
| .restricted = false, | |
| .in_enabled = true, | |
| .vnetwork = net, | |
| .vnetmask = mask, | |
| .vhost = host, | |
| .in6_enabled = true, | |
| .vprefix_addr6 = ip6_prefix, | |
| .vprefix_len = vprefix6_len, | |
| .vhost6 = ip6_host, | |
| .tftp_path = "fuzzing/tftp", | |
| .vdhcp_start = dhcp, | |
| .vnameserver = dns, | |
| .vnameserver6 = ip6_dns, | |
| }; | |
| slirp = slirp_new(&cfg, &slirp_cb, NULL); | |
| slirp_add_exec(slirp, "cat", &fwd, 1234); | |
| for ( ; size > sizeof(*rec); data += rec->incl_len, size -= rec->incl_len) { | |
| rec = (const void *)data; | |
| data += sizeof(*rec); | |
| size -= sizeof(*rec); | |
| if (rec->incl_len != rec->orig_len) { | |
| g_debug("unsupported rec->incl_len != rec->orig_len"); | |
| break; | |
| } | |
| if (rec->incl_len > size) { | |
| break; | |
| } | |
| if (rec->incl_len >= 14) { | |
| if (data[12] == 0x08 && data[13] == 0x00) { | |
| /* IPv4 */ | |
| if (rec->incl_len >= 14 + 16) { | |
| uint32_t ipsource = * (uint32_t*) (data + 14 + 12); | |
| // This an answer, which we will produce, so don't receive | |
| if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) | |
| continue; | |
| } | |
| } else if (data[12] == 0x86 && data[13] == 0xdd) { | |
| if (rec->incl_len >= 14 + 24) { | |
| struct in6_addr *ipsource = (struct in6_addr *) (data + 14 + 8); | |
| // This an answer, which we will produce, so don't receive | |
| if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) | |
| continue; | |
| } | |
| } | |
| } | |
| slirp_input(slirp, data, rec->incl_len); | |
| slirp_pollfds_fill_socket(slirp, &timeout, add_poll_cb, NULL); | |
| slirp_pollfds_poll(slirp, 0, get_revents_cb, NULL); | |
| } | |
| slirp_cleanup(slirp); | |
| return 0; | |
| } |