[PATCH uci 1/6] tests: add libFuzzer based fuzzing

Petr Štetiar ynezz at true.cz
Sat Oct 3 03:48:25 EDT 2020


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 at true.cz>
---
 tests/CMakeLists.txt                          |   4 +
 tests/fuzz/CMakeLists.txt                     |  18 ++
 .../231ee80a172b8e1749b9d91867989d88e4faf7bb  | Bin 0 -> 15 bytes
 .../26a6253fc1eb695b61a2fc7640ee4c03c19e438e  | Bin 0 -> 9 bytes
 .../29a6e206439d792afba5e8e9c1fdf55e65a1145d  | Bin 0 -> 4 bytes
 .../51045ac5401085f5727c6d3c1cac5f8cc32a2927  |   1 +
 .../845dcf3f15f3c28235e6be148a690e7f03b07f65  | Bin 0 -> 22 bytes
 .../bb589d0621e5472f470fa3425a234c74b1e202e8  |   1 +
 .../ea387894a296772f96706df8b999a52d9334c746  | Bin 0 -> 5 bytes
 ...d:000000,sig:11,src:000001,op:flip1,pos:24 | Bin 0 -> 44 bytes
 ...sig:11,src:000022,op:arith8,pos:42,val:+26 | Bin 0 -> 43 bytes
 tests/fuzz/dict/uci.dict                      |  18 ++
 tests/fuzz/inputs/dhcp                        |  49 +++++
 tests/fuzz/inputs/firewall                    | 208 ++++++++++++++++++
 tests/fuzz/test-fuzz.c                        |  60 +++++
 15 files changed, 359 insertions(+)
 create mode 100644 tests/fuzz/CMakeLists.txt
 create mode 100644 tests/fuzz/corpus/231ee80a172b8e1749b9d91867989d88e4faf7bb
 create mode 100644 tests/fuzz/corpus/26a6253fc1eb695b61a2fc7640ee4c03c19e438e
 create mode 100644 tests/fuzz/corpus/29a6e206439d792afba5e8e9c1fdf55e65a1145d
 create mode 100644 tests/fuzz/corpus/51045ac5401085f5727c6d3c1cac5f8cc32a2927
 create mode 100644 tests/fuzz/corpus/845dcf3f15f3c28235e6be148a690e7f03b07f65
 create mode 100644 tests/fuzz/corpus/bb589d0621e5472f470fa3425a234c74b1e202e8
 create mode 100644 tests/fuzz/corpus/ea387894a296772f96706df8b999a52d9334c746
 create mode 100644 tests/fuzz/corpus/id:000000,sig:11,src:000001,op:flip1,pos:24
 create mode 100644 tests/fuzz/corpus/id:000008,sig:11,src:000022,op:arith8,pos:42,val:+26
 create mode 100644 tests/fuzz/dict/uci.dict
 create mode 100644 tests/fuzz/inputs/dhcp
 create mode 100644 tests/fuzz/inputs/firewall
 create mode 100644 tests/fuzz/test-fuzz.c

diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 872ed6de12d4..6f31b9343f5d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -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
index 000000000000..1533c46ca971
--- /dev/null
+++ b/tests/fuzz/CMakeLists.txt
@@ -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
index 0000000000000000000000000000000000000000..9c17457fb85abbcc5b28d861981937b3b2dc0fb2
GIT binary patch
literal 15
Wcmd;N;9}t7WN<9t?B<Q(QU(AEWC8*J

literal 0
HcmV?d00001

diff --git a/tests/fuzz/corpus/26a6253fc1eb695b61a2fc7640ee4c03c19e438e b/tests/fuzz/corpus/26a6253fc1eb695b61a2fc7640ee4c03c19e438e
new file mode 100644
index 0000000000000000000000000000000000000000..4e71cf69915767a7092e79800fabf341a74fbce3
GIT binary patch
literal 9
Qcmd;N;9}t7WN=gl00El-ivR!s

literal 0
HcmV?d00001

diff --git a/tests/fuzz/corpus/29a6e206439d792afba5e8e9c1fdf55e65a1145d b/tests/fuzz/corpus/29a6e206439d792afba5e8e9c1fdf55e65a1145d
new file mode 100644
index 0000000000000000000000000000000000000000..f065bd7508aa513425a67a22a7e39e3e5fbfea4c
GIT binary patch
literal 4
LcmZSJVo(ME07U>U

literal 0
HcmV?d00001

diff --git a/tests/fuzz/corpus/51045ac5401085f5727c6d3c1cac5f8cc32a2927 b/tests/fuzz/corpus/51045ac5401085f5727c6d3c1cac5f8cc32a2927
new file mode 100644
index 000000000000..f47ebca01bc0
--- /dev/null
+++ b/tests/fuzz/corpus/51045ac5401085f5727c6d3c1cac5f8cc32a2927
@@ -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
index 0000000000000000000000000000000000000000..cf5c1b00e262ac0b3fcf9c8c7ae43099d4f8488e
GIT binary patch
literal 22
Tcmd;N;NnC=1)K)FF<i<33s(W3

literal 0
HcmV?d00001

diff --git a/tests/fuzz/corpus/bb589d0621e5472f470fa3425a234c74b1e202e8 b/tests/fuzz/corpus/bb589d0621e5472f470fa3425a234c74b1e202e8
new file mode 100644
index 000000000000..ad2823b48f78
--- /dev/null
+++ b/tests/fuzz/corpus/bb589d0621e5472f470fa3425a234c74b1e202e8
@@ -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
index 0000000000000000000000000000000000000000..2a72a978d4f0beaee9eb9d174e591243272d7413
GIT binary patch
literal 5
Mcmd;N;9^h)0046UHvj+t

literal 0
HcmV?d00001

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
index 0000000000000000000000000000000000000000..3f4e40d09a685f9641cc1e1ebea1f264ab1080dc
GIT binary patch
literal 44
dcmXS5;NoQ9GQdCz3|t at y2rA$-;Emzp1OQDZ1AG7g

literal 0
HcmV?d00001

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
index 0000000000000000000000000000000000000000..20bc48d514b1ad3d25ee34221f2de2586c77c205
GIT binary patch
literal 43
Tcmd;N;Nm2N3OEgTW4M$79Et)Y

literal 0
HcmV?d00001

diff --git a/tests/fuzz/dict/uci.dict b/tests/fuzz/dict/uci.dict
new file mode 100644
index 000000000000..73c86ecbe7c1
--- /dev/null
+++ b/tests/fuzz/dict/uci.dict
@@ -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
index 000000000000..2bc0d312207d
--- /dev/null
+++ b/tests/fuzz/inputs/dhcp
@@ -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
index 000000000000..5e22f984ce9f
--- /dev/null
+++ b/tests/fuzz/inputs/firewall
@@ -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
index 000000000000..9946a5786ce1
--- /dev/null
+++ b/tests/fuzz/test-fuzz.c
@@ -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;
+}



More information about the openwrt-devel mailing list