aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2024-04-25 20:24:15 +0200
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2024-04-25 20:34:55 +0200
commitab0db890a9857e27acd894ce659e98e72d429ef4 (patch)
treefe8ed95d992391fef903bd9f49df3221e9c23734
parent0b3155e18fa3279cce841b9828cd4feab5e5b7a8 (diff)
downloadslirp-ab0db890a9857e27acd894ce659e98e72d429ef4.zip
slirp-ab0db890a9857e27acd894ce659e98e72d429ef4.tar.gz
slirp-ab0db890a9857e27acd894ce659e98e72d429ef4.tar.bz2
fuzz: Add IPv6 cases
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--fuzzing/IN_icmp6/ping_10-0-2-2.pcapbin0 -> 292 bytes
l---------fuzzing/IN_ip6-header/DNS_freedesktop_1-1-1-1.pcap1
l---------fuzzing/IN_ip6-header/ping_10-0-2-2.pcap1
l---------fuzzing/IN_ip6-header/tcp_qemucapt.pcap1
l---------fuzzing/IN_ip6-header/tftp_capture.pcap1
l---------fuzzing/IN_ip6-header/tftp_get_libslirp-txt.pcap1
l---------fuzzing/IN_tcp6-d1
l---------fuzzing/IN_tcp6-h1
-rw-r--r--fuzzing/IN_tcp6/tcp_qemucapt.pcapbin0 -> 9758 bytes
-rw-r--r--fuzzing/IN_tftp6/tftp_capture.pcapbin0 -> 1907 bytes
-rw-r--r--fuzzing/IN_tftp6/tftp_get_libslirp-txt.pcapbin0 -> 302 bytes
l---------fuzzing/IN_udp6-h1
-rw-r--r--fuzzing/IN_udp6/DNS_freedesktop_1-1-1-1.pcapbin0 -> 296 bytes
l---------fuzzing/IN_udp6/tftp_capture.pcap1
l---------fuzzing/IN_udp6/tftp_get_libslirp-txt.pcap1
-rw-r--r--fuzzing/helper.c31
-rw-r--r--fuzzing/helper.h4
-rw-r--r--fuzzing/meson.build10
-rwxr-xr-xfuzzing/oss-fuzz.sh7
-rw-r--r--fuzzing/slirp_fuzz_icmp6.c122
-rw-r--r--fuzzing/slirp_fuzz_ip6_header.c103
-rw-r--r--fuzzing/slirp_fuzz_tcp6.c137
-rw-r--r--fuzzing/slirp_fuzz_tcp6_data.c140
-rw-r--r--fuzzing/slirp_fuzz_tcp6_header.c139
-rw-r--r--fuzzing/slirp_fuzz_udp6.c120
-rw-r--r--fuzzing/slirp_fuzz_udp6_data.c122
-rw-r--r--fuzzing/slirp_fuzz_udp6_header.c121
-rw-r--r--src/ip6.h2
29 files changed, 1060 insertions, 10 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3a2c204..a1038b6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -39,7 +39,7 @@ build-usan:
fuzz:
parallel:
matrix:
- - TARGET: [ip-header, udp, udp-h, tftp, dhcp, icmp, tcp, tcp-h, tcp-d]
+ - TARGET: [ip-header, udp, udp-h, tftp, dhcp, icmp, tcp, tcp-h, ip6-header, udp6, udp6-h, tftp6, icmp6, tcp6, tcp6-h]
script:
- CC=clang CXX=clang++ meson build -Dllvm-fuzz=true || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
diff --git a/fuzzing/IN_icmp6/ping_10-0-2-2.pcap b/fuzzing/IN_icmp6/ping_10-0-2-2.pcap
new file mode 100644
index 0000000..87f6389
--- /dev/null
+++ b/fuzzing/IN_icmp6/ping_10-0-2-2.pcap
Binary files differ
diff --git a/fuzzing/IN_ip6-header/DNS_freedesktop_1-1-1-1.pcap b/fuzzing/IN_ip6-header/DNS_freedesktop_1-1-1-1.pcap
new file mode 120000
index 0000000..651c273
--- /dev/null
+++ b/fuzzing/IN_ip6-header/DNS_freedesktop_1-1-1-1.pcap
@@ -0,0 +1 @@
+../IN_udp6/DNS_freedesktop_1-1-1-1.pcap \ No newline at end of file
diff --git a/fuzzing/IN_ip6-header/ping_10-0-2-2.pcap b/fuzzing/IN_ip6-header/ping_10-0-2-2.pcap
new file mode 120000
index 0000000..632a286
--- /dev/null
+++ b/fuzzing/IN_ip6-header/ping_10-0-2-2.pcap
@@ -0,0 +1 @@
+../IN_icmp6/ping_10-0-2-2.pcap \ No newline at end of file
diff --git a/fuzzing/IN_ip6-header/tcp_qemucapt.pcap b/fuzzing/IN_ip6-header/tcp_qemucapt.pcap
new file mode 120000
index 0000000..b444205
--- /dev/null
+++ b/fuzzing/IN_ip6-header/tcp_qemucapt.pcap
@@ -0,0 +1 @@
+../IN_tcp6/tcp_qemucapt.pcap \ No newline at end of file
diff --git a/fuzzing/IN_ip6-header/tftp_capture.pcap b/fuzzing/IN_ip6-header/tftp_capture.pcap
new file mode 120000
index 0000000..308ab28
--- /dev/null
+++ b/fuzzing/IN_ip6-header/tftp_capture.pcap
@@ -0,0 +1 @@
+../IN_udp6/tftp_capture.pcap \ No newline at end of file
diff --git a/fuzzing/IN_ip6-header/tftp_get_libslirp-txt.pcap b/fuzzing/IN_ip6-header/tftp_get_libslirp-txt.pcap
new file mode 120000
index 0000000..d3b4e76
--- /dev/null
+++ b/fuzzing/IN_ip6-header/tftp_get_libslirp-txt.pcap
@@ -0,0 +1 @@
+../IN_udp6/tftp_get_libslirp-txt.pcap \ No newline at end of file
diff --git a/fuzzing/IN_tcp6-d b/fuzzing/IN_tcp6-d
new file mode 120000
index 0000000..2ad3459
--- /dev/null
+++ b/fuzzing/IN_tcp6-d
@@ -0,0 +1 @@
+IN_tcp6 \ No newline at end of file
diff --git a/fuzzing/IN_tcp6-h b/fuzzing/IN_tcp6-h
new file mode 120000
index 0000000..2ad3459
--- /dev/null
+++ b/fuzzing/IN_tcp6-h
@@ -0,0 +1 @@
+IN_tcp6 \ No newline at end of file
diff --git a/fuzzing/IN_tcp6/tcp_qemucapt.pcap b/fuzzing/IN_tcp6/tcp_qemucapt.pcap
new file mode 100644
index 0000000..d7936b3
--- /dev/null
+++ b/fuzzing/IN_tcp6/tcp_qemucapt.pcap
Binary files differ
diff --git a/fuzzing/IN_tftp6/tftp_capture.pcap b/fuzzing/IN_tftp6/tftp_capture.pcap
new file mode 100644
index 0000000..dd3ee71
--- /dev/null
+++ b/fuzzing/IN_tftp6/tftp_capture.pcap
Binary files differ
diff --git a/fuzzing/IN_tftp6/tftp_get_libslirp-txt.pcap b/fuzzing/IN_tftp6/tftp_get_libslirp-txt.pcap
new file mode 100644
index 0000000..caa144a
--- /dev/null
+++ b/fuzzing/IN_tftp6/tftp_get_libslirp-txt.pcap
Binary files differ
diff --git a/fuzzing/IN_udp6-h b/fuzzing/IN_udp6-h
new file mode 120000
index 0000000..4d7837b
--- /dev/null
+++ b/fuzzing/IN_udp6-h
@@ -0,0 +1 @@
+IN_udp6 \ No newline at end of file
diff --git a/fuzzing/IN_udp6/DNS_freedesktop_1-1-1-1.pcap b/fuzzing/IN_udp6/DNS_freedesktop_1-1-1-1.pcap
new file mode 100644
index 0000000..87abc12
--- /dev/null
+++ b/fuzzing/IN_udp6/DNS_freedesktop_1-1-1-1.pcap
Binary files differ
diff --git a/fuzzing/IN_udp6/tftp_capture.pcap b/fuzzing/IN_udp6/tftp_capture.pcap
new file mode 120000
index 0000000..9bd6830
--- /dev/null
+++ b/fuzzing/IN_udp6/tftp_capture.pcap
@@ -0,0 +1 @@
+../IN_tftp6/tftp_capture.pcap \ No newline at end of file
diff --git a/fuzzing/IN_udp6/tftp_get_libslirp-txt.pcap b/fuzzing/IN_udp6/tftp_get_libslirp-txt.pcap
new file mode 120000
index 0000000..39fc722
--- /dev/null
+++ b/fuzzing/IN_udp6/tftp_get_libslirp-txt.pcap
@@ -0,0 +1 @@
+../IN_tftp6/tftp_get_libslirp-txt.pcap \ No newline at end of file
diff --git a/fuzzing/helper.c b/fuzzing/helper.c
index 101c375..399cfb5 100644
--- a/fuzzing/helper.c
+++ b/fuzzing/helper.c
@@ -2,12 +2,15 @@
#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.
@@ -178,8 +181,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
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;
- struct in6_addr ip6_host;
- struct in6_addr ip6_dns;
int ret, vprefix6_len = 64;
const char *vhostname = NULL;
const char *tftp_server_name = NULL;
@@ -190,7 +191,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
const pcap_hdr_t *hdr = (const void *)data;
const pcaprec_hdr_t *rec = NULL;
uint32_t timeout = 0;
- uint32_t ipsource;
if (size < sizeof(pcap_hdr_t)) {
return 0;
@@ -239,12 +239,25 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
break;
}
- if (rec->incl_len >= 14 + 16) {
- 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;
+ 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);
diff --git a/fuzzing/helper.h b/fuzzing/helper.h
index 5a571a2..92b5f62 100644
--- a/fuzzing/helper.h
+++ b/fuzzing/helper.h
@@ -14,7 +14,11 @@
#include <netinet/in.h>
#define PSEUDO_IP_SIZE (4*2 + 4)
+#define PSEUDO_IPV6_SIZE (16*2 + 4)
uint16_t compute_checksum(uint8_t *Data, size_t Size);
+extern struct in6_addr ip6_host;
+extern struct in6_addr ip6_dns;
+
#endif /* _HELPER_H */
diff --git a/fuzzing/meson.build b/fuzzing/meson.build
index 2bc472a..8e6e4c8 100644
--- a/fuzzing/meson.build
+++ b/fuzzing/meson.build
@@ -29,6 +29,16 @@ exes = [['fuzz-ip-header', ['slirp_fuzz_ip_header.c', 'helper.c']],
['fuzz-tcp-h', ['slirp_fuzz_tcp_header.c', 'helper.c']],
['fuzz-tcp-d', ['slirp_fuzz_tcp_data.c', 'helper.c']],
['fuzz-icmp', ['slirp_fuzz_icmp.c', 'helper.c']],
+
+ ['fuzz-ip6-header', ['slirp_fuzz_ip6_header.c', 'helper.c']],
+ ['fuzz-udp6', ['slirp_fuzz_udp6.c', 'helper.c']],
+ ['fuzz-udp6-h', ['slirp_fuzz_udp6_header.c', 'helper.c']],
+ ['fuzz-udp6-d', ['slirp_fuzz_udp6_data.c', 'helper.c']],
+ ['fuzz-tftp6', ['slirp_fuzz_udp6_data.c', 'helper.c']],
+ ['fuzz-tcp6', ['slirp_fuzz_tcp6.c', 'helper.c']],
+ ['fuzz-tcp6-h', ['slirp_fuzz_tcp6_header.c', 'helper.c']],
+ ['fuzz-tcp6-d', ['slirp_fuzz_tcp6_data.c', 'helper.c']],
+ ['fuzz-icmp6', ['slirp_fuzz_icmp6.c', 'helper.c']],
]
if fuzzer_build
diff --git a/fuzzing/oss-fuzz.sh b/fuzzing/oss-fuzz.sh
index 7ae911e..75a6e64 100755
--- a/fuzzing/oss-fuzz.sh
+++ b/fuzzing/oss-fuzz.sh
@@ -35,5 +35,12 @@ zip -jqr $OUT/fuzz-icmp_seed_corpus.zip "$(dirname "$0")/IN_icmp"
zip -jqr $OUT/fuzz-tcp_seed_corpus.zip "$(dirname "$0")/IN_tcp"
zip -jqr $OUT/fuzz-tcp-h_seed_corpus.zip "$(dirname "$0")/IN_tcp-h"
+zip -jqr $OUT/fuzz-ip6-header_seed_corpus.zip "$(dirname "$0")/IN_ip6-header"
+zip -jqr $OUT/fuzz-udp6_seed_corpus.zip "$(dirname "$0")/IN_udp6"
+zip -jqr $OUT/fuzz-udp6-h_seed_corpus.zip "$(dirname "$0")/IN_udp6-h"
+zip -jqr $OUT/fuzz-tftp6_seed_corpus.zip "$(dirname "$0")/IN_tftp6"
+zip -jqr $OUT/fuzz-icmp6_seed_corpus.zip "$(dirname "$0")/IN_icmp6"
+zip -jqr $OUT/fuzz-tcp6_seed_corpus.zip "$(dirname "$0")/IN_tcp6"
+
find $build -type f -executable -name "fuzz-*" -exec mv {} $OUT \;
find $build -type f -name "*.options" -exec mv {} $OUT \;
diff --git a/fuzzing/slirp_fuzz_icmp6.c b/fuzzing/slirp_fuzz_icmp6.c
new file mode 100644
index 0000000..094769c
--- /dev/null
+++ b/fuzzing/slirp_fuzz_icmp6.c
@@ -0,0 +1,122 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ // Exclude packets that are not ICMP from the mutation strategy
+ if (ip_data[9] != IPPROTO_ICMPV6)
+ continue;
+
+ uint8_t Data_to_mutate[MaxSize];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ uint8_t *start_of_icmp = ip_data + ip_hl_in_bytes;
+ uint16_t icmp_size = ntohs(*(uint16_t *)(ip_data + 4));
+
+ // The size inside the packet can't be trusted, if it is too big it can
+ // lead to heap overflows in the fuzzing code.
+ // Fixme : don't use udp_size inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (icmp_size > MaxSize || icmp_size > rec->incl_len - 14 - ip_hl_in_bytes)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in icmp
+ memset(Data_to_mutate, 0, MaxSize);
+ memcpy(Data_to_mutate, start_of_icmp, icmp_size);
+
+ // Call to libfuzzer's mutation function.
+ // For now we dont want to change the header size as it would require to
+ // resize the `Data` array to include the new bytes inside the whole
+ // packet.
+ // This should be easy as LibFuzzer probably does it by itself or
+ // reserved enough space in Data beforehand, needs some research to
+ // confirm.
+ // FIXME: allow up to grow header size to 60 bytes,
+ // requires to update the `header length` before calculating
+ // checksum
+ LLVMFuzzerMutate(Data_to_mutate, icmp_size, icmp_size);
+
+ // Set the `checksum` field to 0 and calculate the new checksum
+ *(uint16_t *)(Data_to_mutate + 2) = 0;
+ uint16_t new_checksum =
+ compute_checksum(Data_to_mutate, icmp_size);
+ *(uint16_t *)(Data_to_mutate + 2) = htons(new_checksum);
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(start_of_icmp, Data_to_mutate, icmp_size);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/fuzzing/slirp_fuzz_ip6_header.c b/fuzzing/slirp_fuzz_ip6_header.c
new file mode 100644
index 0000000..4714f92
--- /dev/null
+++ b/fuzzing/slirp_fuzz_ip6_header.c
@@ -0,0 +1,103 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ uint8_t Data_to_mutate[MaxSize];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in the ip header, maybe the IPs or
+ // total length should be excluded ?
+ memset(Data_to_mutate, 0, MaxSize);
+ memcpy(Data_to_mutate, ip_data, ip_hl_in_bytes);
+
+ // Call to libfuzzer's mutation function.
+ // For now we dont want to change the header size as it would require to
+ // resize the `Data` array to include the new bytes inside the whole
+ // packet.
+ // This should be easy as LibFuzzer probably does it by itself or
+ // reserved enough space in Data beforehand, needs some research to
+ // confirm.
+ // FIXME: allow up to grow header size to 60 bytes,
+ // requires to update the `header length` before calculating
+ // checksum
+ LLVMFuzzerMutate(Data_to_mutate, ip_hl_in_bytes, ip_hl_in_bytes);
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(ip_data, Data_to_mutate, ip_hl_in_bytes);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/fuzzing/slirp_fuzz_tcp6.c b/fuzzing/slirp_fuzz_tcp6.c
new file mode 100644
index 0000000..1b00755
--- /dev/null
+++ b/fuzzing/slirp_fuzz_tcp6.c
@@ -0,0 +1,137 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ // Exclude packets that are not TCP from the mutation strategy
+ if (ip_data[6] != IPPROTO_TCP)
+ {
+ fprintf(stderr,"wat\n");
+ continue;
+ }
+
+ // Allocate a bit more than needed, this is useful for
+ // checksum calculation.
+ uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
+ uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4));
+
+ // The size inside the packet can't be trusted, if it is too big it can
+ // lead to heap overflows in the fuzzing code.
+ // Fixme : don't use tcp_size inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in the tcp packet
+ memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE);
+ memcpy(Data_to_mutate, start_of_tcp, tcp_size);
+
+ // Call to libfuzzer's mutation function.
+ // Pass the whole TCP packet, mutate it and then fix checksum value
+ // so the packet isn't rejected.
+ // The new size of the data is returned by LLVMFuzzerMutate.
+ // Fixme: allow to change the size of the TCP packet, this will require
+ // to fix the size before calculating the new checksum and change
+ // how the Data_ptr is advanced.
+ // Most offsets bellow should be good for when the switch will be
+ // done to avoid overwriting new/mutated data.
+ LLVMFuzzerMutate(Data_to_mutate, tcp_size, tcp_size);
+
+ // Set the `checksum` field to 0 to calculate the new checksum
+
+ *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
+ // Copy the source and destination IP addresses, the tcp length and
+ // protocol number at the end of the `Data_to_mutate` array to calculate
+ // the new checksum.
+ memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2);
+
+ *(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP;
+
+ *(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256);
+ *(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256);
+
+ uint16_t new_checksum =
+ compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE);
+ *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(start_of_tcp, Data_to_mutate, tcp_size);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/fuzzing/slirp_fuzz_tcp6_data.c b/fuzzing/slirp_fuzz_tcp6_data.c
new file mode 100644
index 0000000..c9cf5f7
--- /dev/null
+++ b/fuzzing/slirp_fuzz_tcp6_data.c
@@ -0,0 +1,140 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ // Exclude packets that are not TCP from the mutation strategy
+ if (ip_data[6] != IPPROTO_TCP)
+ {
+ fprintf(stderr,"wat\n");
+ continue;
+ }
+
+ // Allocate a bit more than needed, this is useful for
+ // checksum calculation.
+ uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
+ uint8_t tcp_header_size = 4 * (*(start_of_tcp + 12) >> 4);
+ uint8_t *start_of_data = ip_data + ip_hl_in_bytes + tcp_header_size;
+ uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4));
+ uint16_t tcp_data_size = (tcp_size - (uint16_t)tcp_header_size);
+
+ // The size inside the packet can't be trusted, if it is too big it can
+ // lead to heap overflows in the fuzzing code.
+ // Fixme : don't use tcp_size inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (tcp_data_size > MaxSize || tcp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - tcp_header_size)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in the tcp packet
+ memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE);
+ memcpy(Data_to_mutate, start_of_tcp, tcp_size);
+
+ // Call to libfuzzer's mutation function.
+ // Pass the whole TCP packet, mutate it and then fix checksum value
+ // so the packet isn't rejected.
+ // The new size of the data is returned by LLVMFuzzerMutate.
+ // Fixme: allow to change the size of the TCP packet, this will require
+ // to fix the size before calculating the new checksum and change
+ // how the Data_ptr is advanced.
+ // Most offsets bellow should be good for when the switch will be
+ // done to avoid overwriting new/mutated data.
+ LLVMFuzzerMutate(Data_to_mutate + tcp_header_size, tcp_data_size, tcp_data_size);
+
+ // Set the `checksum` field to 0 to calculate the new checksum
+
+ *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
+ // Copy the source and destination IP addresses, the tcp length and
+ // protocol number at the end of the `Data_to_mutate` array to calculate
+ // the new checksum.
+ memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2);
+
+ *(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP;
+
+ *(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256);
+ *(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256);
+
+ uint16_t new_checksum =
+ compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE);
+ *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(start_of_tcp, Data_to_mutate, tcp_size);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/fuzzing/slirp_fuzz_tcp6_header.c b/fuzzing/slirp_fuzz_tcp6_header.c
new file mode 100644
index 0000000..9205fc4
--- /dev/null
+++ b/fuzzing/slirp_fuzz_tcp6_header.c
@@ -0,0 +1,139 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ // Exclude packets that are not TCP from the mutation strategy
+ if (ip_data[6] != IPPROTO_TCP)
+ {
+ fprintf(stderr,"wat\n");
+ continue;
+ }
+
+ // Allocate a bit more than needed, this is useful for
+ // checksum calculation.
+ uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
+ uint8_t tcp_header_size = (*(start_of_tcp + 12) >> 4) * 4;
+ uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4));
+
+ // The size inside the packet can't be trusted, if it is too big it can
+ // lead to heap overflows in the fuzzing code.
+ // Fixme : don't use tcp_size inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes ||
+ tcp_header_size > MaxSize || tcp_header_size > rec->incl_len - 14 - ip_hl_in_bytes)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in the tcp packet
+ memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE);
+ memcpy(Data_to_mutate, start_of_tcp, tcp_size);
+
+ // Call to libfuzzer's mutation function.
+ // Pass the whole TCP packet, mutate it and then fix checksum value
+ // so the packet isn't rejected.
+ // The new size of the data is returned by LLVMFuzzerMutate.
+ // Fixme: allow to change the size of the TCP packet, this will require
+ // to fix the size before calculating the new checksum and change
+ // how the Data_ptr is advanced.
+ // Most offsets bellow should be good for when the switch will be
+ // done to avoid overwriting new/mutated data.
+ LLVMFuzzerMutate(Data_to_mutate, tcp_header_size, tcp_header_size);
+
+ // Set the `checksum` field to 0 to calculate the new checksum
+
+ *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
+ // Copy the source and destination IP addresses, the tcp length and
+ // protocol number at the end of the `Data_to_mutate` array to calculate
+ // the new checksum.
+ memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2);
+
+ *(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP;
+
+ *(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256);
+ *(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256);
+
+ uint16_t new_checksum =
+ compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE);
+ *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(start_of_tcp, Data_to_mutate, tcp_size);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/fuzzing/slirp_fuzz_udp6.c b/fuzzing/slirp_fuzz_udp6.c
new file mode 100644
index 0000000..4b00de7
--- /dev/null
+++ b/fuzzing/slirp_fuzz_udp6.c
@@ -0,0 +1,120 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ // Exclude packets that are not UDP from the mutation strategy
+ if (ip_data[6] != IPPROTO_UDP)
+ continue;
+
+ uint8_t Data_to_mutate[MaxSize];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
+ uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4));
+
+ // The size inside the packet can't be trusted, if it is too big it can
+ // lead to heap overflows in the fuzzing code.
+ // Fixme : don't use udp_size inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in the udp packet
+ memset(Data_to_mutate, 0, MaxSize);
+ memcpy(Data_to_mutate, start_of_udp, udp_size);
+
+ // Call to libfuzzer's mutation function.
+ // Pass the whole UDP packet, mutate it and then fix checksum value
+ // so the packet isn't rejected.
+ // The new size of the data is returned by LLVMFuzzerMutate.
+ // Fixme: allow to change the size of the UDP packet, this will require
+ // to fix the size before calculating the new checksum and change
+ // how the Data_ptr is advanced.
+ // Most offsets bellow should be good for when the switch will be
+ // done to avoid overwriting new/mutated data.
+ LLVMFuzzerMutate(Data_to_mutate, udp_size, udp_size);
+
+ // Drop checksum
+ // Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't
+ // check that actually
+ *(uint16_t *)(Data_to_mutate + 6) = 0;
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(start_of_udp, Data_to_mutate, udp_size);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/fuzzing/slirp_fuzz_udp6_data.c b/fuzzing/slirp_fuzz_udp6_data.c
new file mode 100644
index 0000000..8306855
--- /dev/null
+++ b/fuzzing/slirp_fuzz_udp6_data.c
@@ -0,0 +1,122 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ // Exclude packets that are not UDP from the mutation strategy
+ if (ip_data[6] != IPPROTO_UDP)
+ continue;
+
+ uint8_t Data_to_mutate[MaxSize];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
+ uint8_t udp_header_size = 8;
+ uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4));
+ uint16_t udp_data_size = (udp_size - (uint16_t)udp_header_size);
+
+ // The size inside the packet can't be trusted, if it is too big it can
+ // lead to heap overflows in the fuzzing code.
+ // Fixme : don't use udp_size inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (udp_data_size > MaxSize || udp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - udp_header_size)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in the udp packet
+ memset(Data_to_mutate, 0, MaxSize);
+ memcpy(Data_to_mutate, start_of_udp, udp_size);
+
+ // Call to libfuzzer's mutation function.
+ // Pass the whole UDP packet, mutate it and then fix checksum value
+ // so the packet isn't rejected.
+ // The new size of the data is returned by LLVMFuzzerMutate.
+ // Fixme: allow to change the size of the UDP packet, this will require
+ // to fix the size before calculating the new checksum and change
+ // how the Data_ptr is advanced.
+ // Most offsets bellow should be good for when the switch will be
+ // done to avoid overwriting new/mutated data.
+ LLVMFuzzerMutate(Data_to_mutate + udp_header_size, udp_data_size, udp_data_size);
+
+ // Drop checksum
+ // Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't
+ // check that actually
+ *(uint16_t *)(Data_to_mutate + 6) = 0;
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(start_of_udp, Data_to_mutate, udp_size);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/fuzzing/slirp_fuzz_udp6_header.c b/fuzzing/slirp_fuzz_udp6_header.c
new file mode 100644
index 0000000..a2734b4
--- /dev/null
+++ b/fuzzing/slirp_fuzz_udp6_header.c
@@ -0,0 +1,121 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../src/libslirp.h"
+#include "../src/ip6.h"
+#include "helper.h"
+#include "slirp_base_fuzz.h"
+
+#ifdef CUSTOM_MUTATOR
+extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
+
+/// This is a custom mutator, this allows us to mutate only specific parts of
+/// the input and fix the checksum so the packet isn't rejected for bad reasons.
+extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed)
+{
+ size_t current_size = Size;
+ uint8_t *Data_ptr = Data;
+ uint8_t *ip_data;
+ bool mutated = false;
+
+ pcap_hdr_t *hdr = (void *)Data_ptr;
+ pcaprec_hdr_t *rec = NULL;
+
+ if (current_size < sizeof(pcap_hdr_t)) {
+ return 0;
+ }
+
+ Data_ptr += sizeof(*hdr);
+ current_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;
+ }
+
+ for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
+ rec = (void *)Data_ptr;
+ Data_ptr += sizeof(*rec);
+ current_size -= sizeof(*rec);
+
+ if (rec->incl_len != rec->orig_len) {
+ return 0;
+ }
+ if (rec->incl_len > current_size) {
+ return 0;
+ }
+ if (rec->incl_len < 14 + 1) {
+ return 0;
+ }
+
+ ip_data = Data_ptr + 14;
+
+ if (rec->incl_len >= 14 + 24) {
+ struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
+
+ // This an answer, which we will produce, so don't receive
+ if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
+ continue;
+ }
+
+ // Exclude packets that are not UDP from the mutation strategy
+ if (ip_data[6] != IPPROTO_UDP)
+ continue;
+
+ uint8_t Data_to_mutate[MaxSize];
+ uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
+
+ // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (ip_hl_in_bytes > rec->incl_len - 14)
+ return 0;
+
+ uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
+ uint8_t udp_header_size = 8;
+ uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4));
+
+ // The size inside the packet can't be trusted, if it is too big it can
+ // lead to heap overflows in the fuzzing code.
+ // Fixme : don't use udp_size inside the fuzzing code, maybe use the
+ // rec->incl_len and manually calculate the size.
+ if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes)
+ return 0;
+
+ // Copy interesting data to the `Data_to_mutate` array
+ // here we want to fuzz everything in the udp packet
+ memset(Data_to_mutate, 0, MaxSize);
+ memcpy(Data_to_mutate, start_of_udp, udp_size);
+
+ // Call to libfuzzer's mutation function.
+ // Pass the whole UDP packet, mutate it and then fix checksum value
+ // so the packet isn't rejected.
+ // The new size of the data is returned by LLVMFuzzerMutate.
+ // Fixme: allow to change the size of the UDP packet, this will require
+ // to fix the size before calculating the new checksum and change
+ // how the Data_ptr is advanced.
+ // Most offsets bellow should be good for when the switch will be
+ // done to avoid overwriting new/mutated data.
+ LLVMFuzzerMutate(Data_to_mutate, udp_header_size, udp_header_size);
+
+ // Drop checksum
+ // Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't
+ // check that actually
+ *(uint16_t *)(Data_to_mutate + 6) = 0;
+
+ // Copy the mutated data back to the `Data` array
+ memcpy(start_of_udp, Data_to_mutate, udp_size);
+
+ mutated = true;
+ }
+
+ if (!mutated)
+ return 0;
+
+ return Size;
+}
+#endif // CUSTOM_MUTATOR
diff --git a/src/ip6.h b/src/ip6.h
index 870e38b..50765e6 100644
--- a/src/ip6.h
+++ b/src/ip6.h
@@ -10,6 +10,8 @@
#include <glib.h>
#include <string.h>
+#include "util.h"
+
#define ALLNODES_MULTICAST \
{ \
.s6_addr = { \