From 99f3ab144dcaa97a2be37e562740dbff2de350c6 Mon Sep 17 00:00:00 2001
From: Nikias Bassen
Date: Mon, 29 May 2017 04:08:29 +0200
Subject: Integrate fuzzers into build system

---
 Makefile.am          |  3 +++
 configure.ac         | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 fuzz/Makefile.am     | 36 ++++++++++++++++++++++++++
 fuzz/fuzzers.test    |  8 ++++++
 fuzz/init-fuzzers.sh | 23 +++++++++++++++++
 fuzz/test-fuzzers.sh | 33 ++++++++++++++++++++++++
 6 files changed, 175 insertions(+), 1 deletion(-)
 create mode 100644 fuzz/Makefile.am
 create mode 100755 fuzz/fuzzers.test
 create mode 100755 fuzz/init-fuzzers.sh
 create mode 100755 fuzz/test-fuzzers.sh

diff --git a/Makefile.am b/Makefile.am
index 670e7a5..5f36fd7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,9 @@ SUBDIRS = libcnary src include tools test
 if HAVE_CYTHON
 SUBDIRS += cython
 endif
+if BUILD_FUZZERS
+SUBDIRS += fuzz
+endif
 
 docs/html: $(top_builddir)/doxygen.cfg $(top_srcdir)/include/plist/*.h
 	rm -rf docs
diff --git a/configure.ac b/configure.ac
index 1ad1341..43017bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -156,6 +156,76 @@ case "$GLOBAL_CFLAGS" in
         AC_DEFINE([HAVE_FVISIBILITY], [1], [Define if compiled with -fvisibility=hidden])
 esac
 
+AC_ARG_WITH([fuzzers],
+            [AS_HELP_STRING([--with-fuzzers],
+            [build fuzzers and libplist with sanitizers (default is no)])],
+            [build_fuzzers=true],
+            [build_fuzzers=false])
+if test "x$build_fuzzers" = "xtrue"; then
+    AS_COMPILER_FLAG([-fsanitize=address], [
+        SANITIZER_FLAGS+=" -fsanitize=address"
+        ASAN_AVAILABLE=yes
+    ], [])
+    if test "$ASAN_AVAILABLE" = "yes"; then
+        AS_COMPILER_FLAG([-fsanitize=address -fsanitize-address-use-after-scope], [
+            SANITIZER_FLAGS+=" -fsanitize-address-use-after-scope"
+        ], [])
+        SANITIZERS+="ASAN "
+    fi
+
+    AS_COMPILER_FLAG([-fsanitize=undefined], [
+        SANITIZER_FLAGS+=" -fsanitize=undefined"
+        UBSAN_AVAILABLE=yes
+    ], [])
+
+    if test "$UBSAN_AVAILABLE" = "yes"; then
+        SANITIZERS+="UBSAN "
+    fi
+
+    if test -z "$SANITIZER_FLAGS"; then
+        AC_MSG_ERROR([compiler doesn't support -fsanitize=address nor -fsanitize=undefined])
+    fi
+
+    COVERAGE_CHECKS="trace-pc-guard trace-cmp edge"
+    for COV_CHECK in $COVERAGE_CHECKS; do
+        AS_COMPILER_FLAG([-fsanitize-coverage=$COV_CHECK], [
+            if test -z "$SAN_COV_FLAGS"; then
+                SAN_COV_FLAGS="$COV_CHECK"
+            else
+                SAN_COV_FLAGS+=",$COV_CHECK"
+            fi
+        ], [])
+    done
+    if test -n "$SAN_COV_FLAGS"; then
+        SANITIZER_FLAGS+=" -fsanitize-coverage=$SAN_COV_FLAGS"
+    else
+        AC_MSG_WARN([No sanitizer coverage supported by compiler])
+    fi
+
+    CFLAGS="-O1"
+
+    AS_COMPILER_FLAG([-fno-omit-frame-pointer], [
+        CFLAGS+=" -fno-omit-frame-pointer"
+    ], [])
+
+    AS_COMPILER_FLAG([-gline-tables-only], [
+        CFLAGS+=" -gline-tables-only"
+    ],
+    [
+        CFLAGS+=" -g"
+    ])
+
+    CFLAGS+=" -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
+    CFLAGS+=" $SANITIZER_FLAGS"
+    CXXFLAGS="$CFLAGS -std=c++11"
+    EXTRA_CONF+="  Build fuzzers ...........: yes
+"
+    EXTRA_CONF+="  Enabled sanitizers ......: $SANITIZERS
+"
+    AS_COMPILER_FLAGS(TEST_CFLAGS, [$CFLAGS])
+fi
+AM_CONDITIONAL([BUILD_FUZZERS],[test "x$build_fuzzers" = "xtrue"])
+
 m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
 
 AC_OUTPUT([
@@ -168,6 +238,7 @@ include/Makefile
 tools/Makefile
 cython/Makefile
 test/Makefile
+fuzz/Makefile
 doxygen.cfg
 ])
 
@@ -178,7 +249,7 @@ Configuration for $PACKAGE $VERSION:
   Install prefix ..........: $prefix
   Debug code ..............: $debug
   Python bindings .........: $cython_python_bindings
-
+$EXTRA_CONF
   Now type 'make' to build $PACKAGE $VERSION,
   and then 'make install' for installation.
 "
diff --git a/fuzz/Makefile.am b/fuzz/Makefile.am
new file mode 100644
index 0000000..b9798f9
--- /dev/null
+++ b/fuzz/Makefile.am
@@ -0,0 +1,36 @@
+if BUILD_FUZZERS
+
+libFuzzer.a: Fuzzer/build.sh
+	@echo "Building $@"
+	@./Fuzzer/build.sh
+
+Fuzzer/build.sh: LIBFUZZER_SRC
+
+LIBFUZZER_SRC:
+	@if test -d Fuzzer ; then \
+		if test -d Fuzzer/.git ; then \
+			echo Making sure libFuzzer source tree is up-to-date... ; \
+			cd Fuzzer && git checkout . && git pull && cd .. ; \
+		fi \
+	else \
+		echo Checking out libFuzzer source code... ; \
+		git clone https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer ; \
+	fi
+
+CLEANFILES = libFuzzer.a
+
+noinst_PROGRAMS = xplist_fuzzer bplist_fuzzer
+
+xplist_fuzzer_SOURCES = xplist_fuzzer.cc
+xplist_fuzzer_LDFLAGS = -static
+xplist_fuzzer_LDADD = $(top_builddir)/src/libplist.la libFuzzer.a
+
+bplist_fuzzer_SOURCES = bplist_fuzzer.cc
+bplist_fuzzer_LDFLAGS = -static
+bplist_fuzzer_LDADD = $(top_builddir)/src/libplist.la libFuzzer.a
+
+TESTS = fuzzers.test
+
+EXTRA_DIST = bplist.dict xplist.dict init-fuzzers.sh test-fuzzers.sh fuzzers.test
+
+endif
diff --git a/fuzz/fuzzers.test b/fuzz/fuzzers.test
new file mode 100755
index 0000000..dd3fb08
--- /dev/null
+++ b/fuzz/fuzzers.test
@@ -0,0 +1,8 @@
+## -*- sh -*-
+
+set -e
+
+./init-fuzzers.sh
+
+./test-fuzzers.sh
+
diff --git a/fuzz/init-fuzzers.sh b/fuzz/init-fuzzers.sh
new file mode 100755
index 0000000..e48baa8
--- /dev/null
+++ b/fuzz/init-fuzzers.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+CURDIR=`pwd`
+FUZZDIR=`dirname $0`
+
+cd ${FUZZDIR}
+
+if ! test -x xplist_fuzzer || ! test -x bplist_fuzzer; then
+	echo "ERROR: you need to build the fuzzers first."
+	cd ${CURDIR}
+	exit 1
+fi
+
+mkdir -p xplist-input
+cp ../test/data/*.plist xplist-input/
+./xplist_fuzzer -merge=1 xplist-input crashes leaks -dict=xplist.dict
+
+mkdir -p bplist-input
+cp ../test/data/*.bplist bplist-input/
+./bplist_fuzzer -merge=1 bplist-input crashes leaks -dict=bplist.dict
+
+cd ${CURDIR}
+exit 0
diff --git a/fuzz/test-fuzzers.sh b/fuzz/test-fuzzers.sh
new file mode 100755
index 0000000..5c758c4
--- /dev/null
+++ b/fuzz/test-fuzzers.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+CURDIR=`pwd`
+FUZZDIR=`dirname $0`
+
+cd ${FUZZDIR}
+
+if ! test -x xplist_fuzzer || ! test -x bplist_fuzzer; then
+	echo "ERROR: you need to build the fuzzers first."
+	cd ${CURDIR}
+	exit 1
+fi
+
+if ! test -d xplist-input || ! test -d bplist-input; then
+	echo "ERROR: fuzzer corpora directories are not present. Did you run init-fuzzers.sh ?"
+	cd ${CURDIR}
+	exit 1
+fi
+
+echo "### TESTING xplist_fuzzer ###"
+if ! ./xplist_fuzzer xplist-input -dict=xplist.dict -runs=10000; then
+	cd ${CURDIR}
+	exit 1
+fi
+
+echo "### TESTING bplist_fuzzer ###"
+if ! ./bplist_fuzzer bplist-input -dict=bplist.dict -runs=10000; then
+	cd ${CURDIR}
+	exit 1
+fi
+
+cd ${CURDIR}
+exit 0
-- 
cgit v1.1-32-gdbae