tests: add libFuzzer based fuzzing
authorPetr Štetiar <ynezz@true.cz>
Fri, 2 Oct 2020 23:53:53 +0000 (01:53 +0200)
committerPetr Štetiar <ynezz@true.cz>
Sat, 3 Oct 2020 07:20:48 +0000 (09:20 +0200)
LibFuzzer is in-process, coverage-guided, evolutionary fuzzing engine.

LibFuzzer is linked with the library under test, and feeds fuzzed inputs
to the library via a specific fuzzing entrypoint (aka "target
function"); the fuzzer then tracks which areas of the code are reached,
and generates mutations on the corpus of input data in order to maximize
the code coverage.

So lets use libFuzzer to fuzz uci_import for the start.

Ref: https://llvm.org/docs/LibFuzzer.html
Signed-off-by: Petr Štetiar <ynezz@true.cz>
15 files changed:
tests/CMakeLists.txt
tests/fuzz/CMakeLists.txt [new file with mode: 0644]
tests/fuzz/corpus/231ee80a172b8e1749b9d91867989d88e4faf7bb [new file with mode: 0644]
tests/fuzz/corpus/26a6253fc1eb695b61a2fc7640ee4c03c19e438e [new file with mode: 0644]
tests/fuzz/corpus/29a6e206439d792afba5e8e9c1fdf55e65a1145d [new file with mode: 0644]
tests/fuzz/corpus/51045ac5401085f5727c6d3c1cac5f8cc32a2927 [new file with mode: 0644]
tests/fuzz/corpus/845dcf3f15f3c28235e6be148a690e7f03b07f65 [new file with mode: 0644]
tests/fuzz/corpus/bb589d0621e5472f470fa3425a234c74b1e202e8 [new file with mode: 0644]
tests/fuzz/corpus/ea387894a296772f96706df8b999a52d9334c746 [new file with mode: 0644]
tests/fuzz/corpus/id:000000,sig:11,src:000001,op:flip1,pos:24 [new file with mode: 0644]
tests/fuzz/corpus/id:000008,sig:11,src:000022,op:arith8,pos:42,val:+26 [new file with mode: 0644]
tests/fuzz/dict/uci.dict [new file with mode: 0644]
tests/fuzz/inputs/dhcp [new file with mode: 0644]
tests/fuzz/inputs/firewall [new file with mode: 0644]
tests/fuzz/test-fuzz.c [new file with mode: 0644]

index 872ed6de12d415a8d9cce9a5a970a393f4a2ac37..6f31b9343f5d0a31cf470e4cc192863c446c30b9 100644 (file)
@@ -1,2 +1,6 @@
 ADD_SUBDIRECTORY(cram)
 ADD_SUBDIRECTORY(shunit2)
+
+IF(CMAKE_C_COMPILER_ID STREQUAL "Clang")
+  ADD_SUBDIRECTORY(fuzz)
+ENDIF()
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1533c46
--- /dev/null
@@ -0,0 +1,18 @@
+FILE(GLOB test_cases "test-*.c")
+
+MACRO(ADD_FUZZER_TEST name)
+  ADD_EXECUTABLE(${name} ${name}.c)
+  TARGET_COMPILE_OPTIONS(${name} PRIVATE -g -O1 -fno-omit-frame-pointer -fsanitize=fuzzer,address,leak,undefined)
+  TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR})
+  TARGET_LINK_OPTIONS(${name} PRIVATE -stdlib=libc++ -fsanitize=fuzzer,address,leak,undefined)
+  TARGET_LINK_LIBRARIES(${name} uci)
+  ADD_TEST(
+    NAME ${name}
+       COMMAND ${name} -max_len=256 -timeout=10 -max_total_time=300 -dict=${CMAKE_CURRENT_SOURCE_DIR}/dict/uci.dict ${CMAKE_CURRENT_SOURCE_DIR}/corpus
+  )
+ENDMACRO(ADD_FUZZER_TEST)
+
+FOREACH(test_case ${test_cases})
+  GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE)
+  ADD_FUZZER_TEST(${test_case})
+ENDFOREACH(test_case)
diff --git a/tests/fuzz/corpus/231ee80a172b8e1749b9d91867989d88e4faf7bb b/tests/fuzz/corpus/231ee80a172b8e1749b9d91867989d88e4faf7bb
new file mode 100644 (file)
index 0000000..9c17457
Binary files /dev/null and b/tests/fuzz/corpus/231ee80a172b8e1749b9d91867989d88e4faf7bb differ
diff --git a/tests/fuzz/corpus/26a6253fc1eb695b61a2fc7640ee4c03c19e438e b/tests/fuzz/corpus/26a6253fc1eb695b61a2fc7640ee4c03c19e438e
new file mode 100644 (file)
index 0000000..4e71cf6
Binary files /dev/null and b/tests/fuzz/corpus/26a6253fc1eb695b61a2fc7640ee4c03c19e438e differ
diff --git a/tests/fuzz/corpus/29a6e206439d792afba5e8e9c1fdf55e65a1145d b/tests/fuzz/corpus/29a6e206439d792afba5e8e9c1fdf55e65a1145d
new file mode 100644 (file)
index 0000000..f065bd7
Binary files /dev/null and b/tests/fuzz/corpus/29a6e206439d792afba5e8e9c1fdf55e65a1145d differ
diff --git a/tests/fuzz/corpus/51045ac5401085f5727c6d3c1cac5f8cc32a2927 b/tests/fuzz/corpus/51045ac5401085f5727c6d3c1cac5f8cc32a2927
new file mode 100644 (file)
index 0000000..f47ebca
--- /dev/null
@@ -0,0 +1 @@
+ #
\ No newline at end of file
diff --git a/tests/fuzz/corpus/845dcf3f15f3c28235e6be148a690e7f03b07f65 b/tests/fuzz/corpus/845dcf3f15f3c28235e6be148a690e7f03b07f65
new file mode 100644 (file)
index 0000000..cf5c1b0
Binary files /dev/null and b/tests/fuzz/corpus/845dcf3f15f3c28235e6be148a690e7f03b07f65 differ
diff --git a/tests/fuzz/corpus/bb589d0621e5472f470fa3425a234c74b1e202e8 b/tests/fuzz/corpus/bb589d0621e5472f470fa3425a234c74b1e202e8
new file mode 100644 (file)
index 0000000..ad2823b
--- /dev/null
@@ -0,0 +1 @@
+'
\ No newline at end of file
diff --git a/tests/fuzz/corpus/ea387894a296772f96706df8b999a52d9334c746 b/tests/fuzz/corpus/ea387894a296772f96706df8b999a52d9334c746
new file mode 100644 (file)
index 0000000..2a72a97
Binary files /dev/null and b/tests/fuzz/corpus/ea387894a296772f96706df8b999a52d9334c746 differ
diff --git a/tests/fuzz/corpus/id:000000,sig:11,src:000001,op:flip1,pos:24 b/tests/fuzz/corpus/id:000000,sig:11,src:000001,op:flip1,pos:24
new file mode 100644 (file)
index 0000000..3f4e40d
Binary files /dev/null and b/tests/fuzz/corpus/id:000000,sig:11,src:000001,op:flip1,pos:24 differ
diff --git a/tests/fuzz/corpus/id:000008,sig:11,src:000022,op:arith8,pos:42,val:+26 b/tests/fuzz/corpus/id:000008,sig:11,src:000022,op:arith8,pos:42,val:+26
new file mode 100644 (file)
index 0000000..20bc48d
Binary files /dev/null and b/tests/fuzz/corpus/id:000008,sig:11,src:000022,op:arith8,pos:42,val:+26 differ
diff --git a/tests/fuzz/dict/uci.dict b/tests/fuzz/dict/uci.dict
new file mode 100644 (file)
index 0000000..73c86ec
--- /dev/null
@@ -0,0 +1,18 @@
+"c"
+"config"
+"p"
+"package"
+"o"
+"option"
+"l"
+"list"
+"'"
+"\""
+" "
+"\x00"
+"\x0a"
+"\x0d"
+"\x09"
+"#"
+";"
+"\\"
diff --git a/tests/fuzz/inputs/dhcp b/tests/fuzz/inputs/dhcp
new file mode 100644 (file)
index 0000000..2bc0d31
--- /dev/null
@@ -0,0 +1,49 @@
+
+config dnsmasq
+       option domainneeded '1'
+       option boguspriv '1'
+       option filterwin2k '0'
+       option localise_queries '1'
+       option rebind_protection '1'
+       option rebind_localhost '1'
+       option local '/lan/'
+       option domain 'lan'
+       option expandhosts '1'
+       option nonegcache '0'
+       option authoritative '1'
+       option readethers '1'
+       option leasefile '/tmp/dhcp.leases'
+       option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto'
+       option nonwildcard '1'
+       option localservice '1'
+
+config dhcp 'lan'
+       option interface 'lan'
+       option start '100'
+       option limit '150'
+       option leasetime '12h'
+       option dhcpv6 'server'
+       option ra 'server'
+       option ra_slaac '1'
+       list ra_flags 'managed-config'
+       list ra_flags 'other-config'
+
+config dhcp 'wan'
+       option interface 'wan'
+       option ignore '1'
+
+config odhcpd 'odhcpd'
+       option maindhcp '0'
+       option leasefile '/tmp/hosts/odhcpd'
+       option leasetrigger '/usr/sbin/odhcpd-update'
+       option loglevel '4'
+
+config host
+        option name 'foo'
+        option ip '192.168.1.90'
+        option mac '20:de:ad:be:ef:99'
+
+config host
+        option name 'moo-ap01'
+        option ip '192.168.1.2'
+        option mac '00:dd:8d:f7:e6:6f'
diff --git a/tests/fuzz/inputs/firewall b/tests/fuzz/inputs/firewall
new file mode 100644 (file)
index 0000000..5e22f98
--- /dev/null
@@ -0,0 +1,208 @@
+config defaults
+       option syn_flood        1
+       option input            ACCEPT
+       option output           ACCEPT
+       option forward          REJECT
+# Uncomment this line to disable ipv6 rules
+#      option disable_ipv6     1
+
+config zone
+       option name             lan
+       list   network          'lan'
+       option input            ACCEPT
+       option output           ACCEPT
+       option forward          ACCEPT
+
+config zone
+       option name             wan
+       list   network          'wan'
+       list   network          'wan6'
+       option input            REJECT
+       option output           ACCEPT
+       option forward          REJECT
+       option masq             1
+       option mtu_fix          1
+
+config forwarding
+       option src              lan
+       option dest             wan
+
+# We need to accept udp packets on port 68,
+# see https://dev.openwrt.org/ticket/4108
+config rule
+       option name             Allow-DHCP-Renew
+       option src              wan
+       option proto            udp
+       option dest_port        68
+       option target           ACCEPT
+       option family           ipv4
+
+# Allow IPv4 ping
+config rule
+       option name             Allow-Ping
+       option src              wan
+       option proto            icmp
+       option icmp_type        echo-request
+       option family           ipv4
+       option target           ACCEPT
+
+config rule
+       option name             Allow-IGMP
+       option src              wan
+       option proto            igmp
+       option family           ipv4
+       option target           ACCEPT
+
+# Allow DHCPv6 replies
+# see https://dev.openwrt.org/ticket/10381
+config rule
+       option name             Allow-DHCPv6
+       option src              wan
+       option proto            udp
+       option src_ip           fc00::/6
+       option dest_ip          fc00::/6
+       option dest_port        546
+       option family           ipv6
+       option target           ACCEPT
+
+config rule
+       option name             Allow-MLD
+       option src              wan
+       option proto            icmp
+       option src_ip           fe80::/10
+       list icmp_type          '130/0'
+       list icmp_type          '131/0'
+       list icmp_type          '132/0'
+       list icmp_type          '143/0'
+       option family           ipv6
+       option target           ACCEPT
+
+# Allow essential incoming IPv6 ICMP traffic
+config rule
+       option name             Allow-ICMPv6-Input
+       option src              wan
+       option proto    icmp
+       list icmp_type          echo-request
+       list icmp_type          echo-reply
+       list icmp_type          destination-unreachable
+       list icmp_type          packet-too-big
+       list icmp_type          time-exceeded
+       list icmp_type          bad-header
+       list icmp_type          unknown-header-type
+       list icmp_type          router-solicitation
+       list icmp_type          neighbour-solicitation
+       list icmp_type          router-advertisement
+       list icmp_type          neighbour-advertisement
+       option limit            1000/sec
+       option family           ipv6
+       option target           ACCEPT
+
+# Allow essential forwarded IPv6 ICMP traffic
+config rule
+       option name             Allow-ICMPv6-Forward
+       option src              wan
+       option dest             *
+       option proto            icmp
+       list icmp_type          echo-request
+       list icmp_type          echo-reply
+       list icmp_type          destination-unreachable
+       list icmp_type          packet-too-big
+       list icmp_type          time-exceeded
+       list icmp_type          bad-header
+       list icmp_type          unknown-header-type
+       option limit            1000/sec
+       option family           ipv6
+       option target           ACCEPT
+
+config rule
+       option name             Allow-IPSec-ESP
+       option src              wan
+       option dest             lan
+       option proto            esp
+       option target           ACCEPT
+
+config rule
+       option name             Allow-ISAKMP
+       option src              wan
+       option dest             lan
+       option dest_port        500
+       option proto            udp
+       option target           ACCEPT
+
+# allow interoperability with traceroute classic
+# note that traceroute uses a fixed port range, and depends on getting
+# back ICMP Unreachables.  if we're operating in DROP mode, it won't
+# work so we explicitly REJECT packets on these ports.
+config rule
+       option name             Support-UDP-Traceroute
+       option src              wan
+       option dest_port        33434:33689
+       option proto            udp
+       option family           ipv4
+       option target           REJECT
+       option enabled          false
+
+# include a file with users custom iptables rules
+config include
+       option path /etc/firewall.user
+
+
+### EXAMPLE CONFIG SECTIONS
+# do not allow a specific ip to access wan
+#config rule
+#      option src              lan
+#      option src_ip   192.168.45.2
+#      option dest             wan
+#      option proto    tcp
+#      option target   REJECT
+
+# block a specific mac on wan
+#config rule
+#      option dest             wan
+#      option src_mac  00:11:22:33:44:66
+#      option target   REJECT
+
+# block incoming ICMP traffic on a zone
+#config rule
+#      option src              lan
+#      option proto    ICMP
+#      option target   DROP
+
+# port redirect port coming in on wan to lan
+#config redirect
+#      option src                      wan
+#      option src_dport        80
+#      option dest                     lan
+#      option dest_ip          192.168.16.235
+#      option dest_port        80
+#      option proto            tcp
+
+# port redirect of remapped ssh port (22001) on wan
+#config redirect
+#      option src              wan
+#      option src_dport        22001
+#      option dest             lan
+#      option dest_port        22
+#      option proto            tcp
+
+### FULL CONFIG SECTIONS
+#config rule
+#      option src              lan
+#      option src_ip   192.168.45.2
+#      option src_mac  00:11:22:33:44:55
+#      option src_port 80
+#      option dest             wan
+#      option dest_ip  194.25.2.129
+#      option dest_port        120
+#      option proto    tcp
+#      option target   REJECT
+
+#config redirect
+#      option src              lan
+#      option src_ip   192.168.45.2
+#      option src_mac  00:11:22:33:44:55
+#      option src_port         1024
+#      option src_dport        80
+#      option dest_ip  194.25.2.129
+#      option dest_port        120
+#      option proto    tcp
diff --git a/tests/fuzz/test-fuzz.c b/tests/fuzz/test-fuzz.c
new file mode 100644 (file)
index 0000000..9946a57
--- /dev/null
@@ -0,0 +1,60 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "uci.h"
+
+static void fuzz_uci_import(const uint8_t *input, size_t size)
+{
+       int r;
+       int fd;
+       FILE *fs = NULL;
+       struct uci_context *ctx = NULL;
+       struct uci_package *package = NULL;
+
+       fd = open("/dev/shm", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
+       if (fd < 0) {
+               perror("unable to create temp file");
+               exit(-1);
+       }
+
+       r = write(fd, input, size);
+       if (r < 0) {
+               perror("unable to write()");
+               exit(-1);
+       }
+
+       fs = fdopen(fd, "r");
+       if (fs == NULL) {
+               perror("unable to fdopen()");
+               exit(-1);
+       }
+
+       fseek(fs, 0L, SEEK_SET);
+
+       ctx = uci_alloc_context();
+       if (ctx == NULL) {
+               perror("unable to uci_alloc_context()");
+               exit(-1);
+       }
+
+       uci_import(ctx, fs, NULL, &package, false);
+       uci_free_context(ctx);
+       close(fd);
+       fclose(fs);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *input, size_t size)
+{
+       fuzz_uci_import(input, size);
+       return 0;
+}