public inbox for elfutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] tests: integrate fuzz-targets into the test suite
@ 2022-03-21 11:21 Evgeny Vereshchagin
  0 siblings, 0 replies; only message in thread
From: Evgeny Vereshchagin @ 2022-03-21 11:21 UTC (permalink / raw)
  To: elfutils-devel; +Cc: Evgeny Vereshchagin

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 32666 bytes --]

[v5]

1) `fuzz-libdwfl` and `fuzz-libelf` were moved from OSS-Fuzz.

2)  The regression testsuite was extended.

3) The OSS-Fuzz build script was removed. It should probably be
kept on OSS-Fuzz at this point.

4) The honggfuzz kludges were removed because
https://github.com/google/honggfuzz/issues/431 was fixed.

[v4]

1) `--enable-afl` was added to make it possible to build and run
the fuzz target with afl-gcc/afl-g++/afl-fuzz. It's compatible
with both AFL and AFL++ and can be tested on Ubuntu by running
the following commands:
```
apt-get install afl++
autoreconf -i -f
./configure --enable-maintainer-mode --enable-afl
make -j$(nproc) V=1
make check V=1 VERBOSE=1 TESTS=run-fuzz-dwfl-core.sh FUZZ_TIME=600
```

It's compatible with ASan/UBsan as well so something like
`--enable-sanitize-address` and `--enable-sanitize-undefined` can
additionally be passed to run the fuzzer under ASan/UBsan.

It was tested on Fedora with AFL (https://github.com/google/AFL) and
on Ubuntu with AFL++ (https://github.com/AFLplusplus/AFLplusplus/).

2) Both `use_afl` and `use_honggfuzz` are now shown among the additional
test features printed by `./configure` to make it easier to figure out
how exactly elfutils is tested.

[v3]

The test handles infinite loops much better now.
In https://sourceware.org/bugzilla/show_bug.cgi?id=28715#c4 it took
it about 5 hours on Packit to discover an infinite loop on 32 bit
platforms because it didn't enforce any timeouts. It was fixed
by passing --tmout_sigvtalrm to honggfuzz (which treats timeouts
as normal crashes) and by additionally running the fuzz target
with timeout -s9.

[v2]

1) At https://sourceware.org/pipermail/elfutils-devel/2021q4/004541.html
it was pointed out that build-fuzzers.sh is too tied to OSS-Fuzz
and while it was kind of decoupled from it as much as possible
in the sense that it was enough to install clang and run the script to build
the fuzz target with libFuzzer it's true that it can't be integrated smoothly
into buildbot for example where gcc is used and various configure options
control what exactly is testsed. To address that, `--enable-honggfuzz` is
introduced. It looks for hfuzz-gcc, hfuzz-g++ and honggfuzz and if
they exist elfutils is built with those wrappers and the fuzz target
is additionally run for a few minutes under honggfuzz to make
regression testing more effective. It was tested on Fedora 35 and
in https://github.com/evverx/elfutils/pull/53 on Ubuntu Focal with both gcc and
clang with and without sanitizers/Valgrind
and with two versions of honggfuzz (including the latest stable version).
To make it work on Ubuntu the following commands should be run
```
apt-get install libbfd-dev libunwind8-dev
git clone https://github.com/google/honggfuzz
cd honggfuzz
git checkout 2.4
make
make PREFIX=/usr install

cd PATH/TO/ELFUTILS
autoreconf -i -f
./configure --enable-maintainer-mode --enable-honggfuzz
make check V=1 VERBOSE=1 # FUZZ_TIME can be optionally passed
```

If hongfuzz is installed elsewhere it's possible to point
configure to it with CC, CXX and HONGGFUZZ
```
./configure CC=path-to-hfuzz-gcc CXX=path-to-hfuzz-g++ HONGGFUZZ=path-to-honggfuzz
```

I decided to use honggfuzz instead of AFL because AFL doesn't seem
to be maintained actively anymore. Other than that I can't seem to
make it work with various combinations of compilers, sanitizers and
so on. But thanks to the way the fuzz target is written it should
be possible to add it eventually by analogy with honggfuzz.

2) fuzz-dwfl-core-corpus was renamed to fuzz-dwfl-core-crashes to
make it more clear that it isn't exaclty a seed corpus.

3) run-strip-g.sh and run-strip-nothing.sh started to compile test programs
using temporary files instead of gcc -xc -. It should address
https://github.com/google/honggfuzz/issues/431 but more generally
autoconf uses temporary files to make sure compiler works so
it seems in general it's safer to rely on compiler features that
are known to work.

4) A comment was added where I tried to expand on why the fuzz target
is written that way.

[v1]
The fuzz target was integrated into OSS-Fuzz in
https://github.com/google/oss-fuzz/pull/6944 and since then it
has been running there continously (uncovering various issues
along the way). It's all well and good but since OSS-Fuzz
is far from the elfutils repository it's unnecessarily hard
to build the fuzz target locally, verify patches and more generally
test new code to make sure that it doesn't introduce new issues (
or reintroduce regressions). This patch aims to address all those
issues by moving the fuzz target into the elfutils repository,
integrating it into the testsuite and also providing a script
that can be used to build full-fledged fuzzers utilizing libFuzzer.
With this patch applied `make check` can be used to make sure
that files kept in tests/fuzz-dwfl-core-corpus don't crash the
code on various architecures.
`--enable-sanitize-{address,undefined}` and/or `--enable-valgrind`
can additionally be used to uncover issues like
https://sourceware.org/bugzilla/show_bug.cgi?id=28685
that don't always manifest themselves in simple segfaults. On top
of all that now the fuzz target can be built and linked against
libFuzzer locally by just running `./tests/build-fuzzers.sh`.

The patch was tested in https://github.com/evverx/elfutils/pull/53 :

* the testsuite was run on aarch64, armhfp, i386, ppc64le, s390x
  and x86_64

* Fedora packages were built on those architectures;

* elfutils was built with both clang and gcc with and without sanitizers
  to make sure the tests pass there;

* `make distcheck` passed;

* coverage reports were built to make sure "static" builds are intact;

* the fuzz target was built and run with ClusterFuzzLite to make sure
  it's still compatible with OSS-Fuzz;

* the code was analyzed by various static analyzers to make sure new alerts
  aren't introduced.

Signed-off-by: Evgeny Vereshchagin <evvers@ya.ru>
---
 ChangeLog                                   |   4 +
 configure.ac                                |  39 ++++++++
 tests/.gitignore                            |   3 +
 tests/ChangeLog                             |   4 +
 tests/Makefile.am                           |  55 ++++++++++-
 tests/fuzz-dwfl-core-crashes/empty          |   0
 tests/fuzz-dwfl-core-crashes/oss-fuzz-41566 | Bin 0 -> 1553 bytes
 tests/fuzz-dwfl-core-crashes/oss-fuzz-41570 | Bin 0 -> 1233 bytes
 tests/fuzz-dwfl-core-crashes/oss-fuzz-41572 | Bin 0 -> 4872 bytes
 tests/fuzz-dwfl-core.c                      |  58 +++++++++++
 tests/fuzz-libdwfl-crashes/oss-fuzz-45629   |   2 +
 tests/fuzz-libdwfl-crashes/oss-fuzz-45634   | Bin 0 -> 140 bytes
 tests/fuzz-libdwfl-crashes/oss-fuzz-45635   | Bin 0 -> 129 bytes
 tests/fuzz-libdwfl-crashes/oss-fuzz-45646   |   1 +
 tests/fuzz-libdwfl.c                        |  50 ++++++++++
 tests/fuzz-libelf-crashes/oss-fuzz-45637    | Bin 0 -> 684 bytes
 tests/fuzz-libelf-crashes/oss-fuzz-45682    | Bin 0 -> 321 bytes
 tests/fuzz-libelf.c                         |  89 +++++++++++++++++
 tests/fuzz-main.c                           |  43 ++++++++
 tests/fuzz.h                                |   9 ++
 tests/run-fuzz-dwfl-core.sh                 |   3 +
 tests/run-fuzz-libdwfl.sh                   |   3 +
 tests/run-fuzz-libelf.sh                    |   3 +
 tests/run-fuzzer.sh                         | 104 ++++++++++++++++++++
 24 files changed, 468 insertions(+), 2 deletions(-)
 create mode 100644 tests/fuzz-dwfl-core-crashes/empty
 create mode 100644 tests/fuzz-dwfl-core-crashes/oss-fuzz-41566
 create mode 100644 tests/fuzz-dwfl-core-crashes/oss-fuzz-41570
 create mode 100644 tests/fuzz-dwfl-core-crashes/oss-fuzz-41572
 create mode 100644 tests/fuzz-dwfl-core.c
 create mode 100644 tests/fuzz-libdwfl-crashes/oss-fuzz-45629
 create mode 100644 tests/fuzz-libdwfl-crashes/oss-fuzz-45634
 create mode 100644 tests/fuzz-libdwfl-crashes/oss-fuzz-45635
 create mode 100644 tests/fuzz-libdwfl-crashes/oss-fuzz-45646
 create mode 100644 tests/fuzz-libdwfl.c
 create mode 100644 tests/fuzz-libelf-crashes/oss-fuzz-45637
 create mode 100644 tests/fuzz-libelf-crashes/oss-fuzz-45682
 create mode 100644 tests/fuzz-libelf.c
 create mode 100644 tests/fuzz-main.c
 create mode 100644 tests/fuzz.h
 create mode 100755 tests/run-fuzz-dwfl-core.sh
 create mode 100755 tests/run-fuzz-libdwfl.sh
 create mode 100755 tests/run-fuzz-libelf.sh
 create mode 100755 tests/run-fuzzer.sh

diff --git a/ChangeLog b/ChangeLog
index 2f46f903..16edd12d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2022-03-22  Evgeny Vereshchagin  <evvers@ya.ru>
+
+	* configure.ac: Add --enable-afl and --enable-honggfuzz.
+
 2021-03-14  Mark Wielaard  <mark@klomp.org>
 
 	* configure.ac: Use AS_HELP_STRING instead of AC_HELP_STRING.
diff --git a/configure.ac b/configure.ac
index 1aff9f30..739fea64 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,43 @@ AS_IF([test "$use_locks" = yes],
 
 AH_TEMPLATE([USE_LOCKS], [Defined if libraries should be thread-safe.])
 
+AC_ARG_ENABLE([afl],
+AS_HELP_STRING([--enable-afl],[run fuzzers with afl]), [use_afl=$enableval], [use_afl=no])
+if test "$use_afl" = yes; then
+    AC_CHECK_PROG(CC, afl-gcc, afl-gcc)
+    if test -z "$CC"; then
+        AC_MSG_ERROR([failed to find afl-gcc])
+    fi
+    AC_CHECK_PROG(CXX, afl-g++, afl-g++)
+    if test -z "$CXX"; then
+        AC_MSG_ERROR([failed to find afl-g++])
+    fi
+    AC_CHECK_PROG(AFL_FUZZ, afl-fuzz, afl-fuzz)
+    if test -z "$AFL_FUZZ"; then
+        AC_MSG_ERROR([failed to find afl-fuzz])
+    fi
+fi
+
+AC_ARG_ENABLE([honggfuzz],
+AS_HELP_STRING([--enable-honggfuzz],[run fuzzers with honggfuzz]), [use_honggfuzz=$enableval], [use_honggfuzz=no])
+if test "$use_honggfuzz" = yes; then
+    if test "$use_afl" = yes; then
+      AC_MSG_ERROR([cannot enable afl and honggfuzz together])
+    fi
+    AC_CHECK_PROG(CC, hfuzz-gcc, hfuzz-gcc)
+    if test -z "$CC"; then
+        AC_MSG_ERROR([failed to find hfuzz-gcc])
+    fi
+    AC_CHECK_PROG(CXX, hfuzz-g++, hfuzz-g++)
+    if test -z "$CXX"; then
+        AC_MSG_ERROR([failed to find hfuzz-g++])
+    fi
+    AC_CHECK_PROG(HONGGFUZZ, honggfuzz, honggfuzz)
+    if test -z "$HONGGFUZZ"; then
+        AC_MSG_ERROR([failed to find honggfuzz])
+    fi
+fi
+
 AC_PROG_CC_C99
 AC_PROG_CXX
 AC_PROG_RANLIB
@@ -848,6 +885,8 @@ AC_MSG_NOTICE([
     debug branch prediction            : ${use_debugpred}
     gprof support                      : ${use_gprof}
     gcov support                       : ${use_gcov}
+    run fuzzers with honggfuzz         : ${use_honggfuzz}
+    run fuzzers with AFL               : ${use_afl}
     run all tests under valgrind       : ${use_valgrind}
     gcc undefined behaviour sanitizer  : ${use_undefined}
     gcc address sanitizer              : ${use_address}
diff --git a/tests/.gitignore b/tests/.gitignore
index 99d04819..0a9f4f44 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -66,6 +66,9 @@
 /find-prologues
 /funcretval
 /funcscopes
+/fuzz-dwfl-core
+/fuzz-libdwfl
+/fuzz-libelf
 /get-aranges
 /get-files
 /get-lines
diff --git a/tests/ChangeLog b/tests/ChangeLog
index c97ed52e..493943af 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2022-03-22  Evgeny Vereshchagin  <evvers@ya.ru>
+
+	* Makefile.am: Integrate three fuzz targets into the testsuite.
+
 2021-12-17  Mark Wielaard  <mark@klomp.org>
 
 	* run-debuginfod-query-retry.sh: Use /bin/sh instead of /bin/ls.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b2da2c83..6d4892a1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -62,6 +62,9 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  getphdrnum leb128 read_unaligned \
 		  msg_tst system-elf-libelf-test \
 		  nvidia_extended_linemap_libdw \
+		  fuzz-dwfl-core \
+		  fuzz-libdwfl \
+		  fuzz-libelf \
 		  $(asm_TESTS)
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
@@ -197,7 +200,10 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	msg_tst system-elf-libelf-test \
 	$(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \
 	run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
-	run-readelf-dw-form-indirect.sh run-strip-largealign.sh
+	run-readelf-dw-form-indirect.sh run-strip-largealign.sh \
+	run-fuzz-dwfl-core.sh \
+	run-fuzz-libdwfl.sh \
+	run-fuzz-libelf.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -580,7 +586,21 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     run-readelf-dw-form-indirect.sh testfile-dw-form-indirect.bz2 \
 	     run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
 	     testfile_nvidia_linemap.bz2 \
-	     testfile-largealign.o.bz2 run-strip-largealign.sh
+	     testfile-largealign.o.bz2 run-strip-largealign.sh \
+	     run-fuzzer.sh \
+	     run-fuzz-dwfl-core.sh \
+	     run-fuzz-libdwfl.sh \
+	     run-fuzz-libelf.sh \
+	     fuzz-dwfl-core-crashes/empty \
+	     fuzz-dwfl-core-crashes/oss-fuzz-41566 \
+	     fuzz-dwfl-core-crashes/oss-fuzz-41570 \
+	     fuzz-dwfl-core-crashes/oss-fuzz-41572 \
+	     fuzz-libdwfl-crashes/oss-fuzz-45629 \
+	     fuzz-libdwfl-crashes/oss-fuzz-45634 \
+	     fuzz-libdwfl-crashes/oss-fuzz-45635 \
+	     fuzz-libdwfl-crashes/oss-fuzz-45646 \
+	     fuzz-libelf-crashes/oss-fuzz-45637 \
+	     fuzz-libelf-crashes/oss-fuzz-45682
 
 
 if USE_VALGRIND
@@ -595,9 +615,13 @@ installed_TESTS_ENVIRONMENT = libdir='$(DESTDIR)$(libdir)'; \
 			      abs_srcdir='$(abs_srcdir)'; \
 			      abs_builddir='$(abs_builddir)'; \
 			      abs_top_builddir='$(abs_top_builddir)'; \
+			      afl_fuzz='$(AFL_FUZZ)'; \
+			      honggfuzz='$(HONGGFUZZ)'; \
 			      export abs_srcdir; export abs_builddir; \
 			      export abs_top_builddir; \
 			      export libdir; export bindir; \
+			      export afl_fuzz; \
+			      export honggfuzz; \
 			      export LC_ALL; export LANG; export VALGRIND_CMD; \
 			      unset DEBUGINFOD_URLS; \
 			      NM='$(NM)'; export NM; \
@@ -609,8 +633,12 @@ TESTS_ENVIRONMENT = LC_ALL=C; LANG=C; VALGRIND_CMD='$(valgrind_cmd)'; \
 		    abs_srcdir='$(abs_srcdir)'; \
 		    abs_builddir='$(abs_builddir)'; \
 		    abs_top_builddir='$(abs_top_builddir)'; \
+		    afl_fuzz='$(AFL_FUZZ)'; \
+		    honggfuzz='$(HONGGFUZZ)'; \
 		    export abs_srcdir; export abs_builddir; \
 		    export abs_top_builddir; \
+		    export afl_fuzz; \
+		    export honggfuzz; \
 		    export LC_ALL; export LANG; export VALGRIND_CMD; \
 		    unset DEBUGINFOD_URLS; \
 		    NM='$(NM)'; export NM; \
@@ -755,6 +783,29 @@ leb128_LDADD = $(libelf) $(libdw)
 read_unaligned_LDADD = $(libelf) $(libdw)
 nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw)
 
+# Fuzz targets are split into two files so that they can be
+# compatible with the test suite, OSS-Fuzz and various fuzzing
+# engines.
+#
+# OSS-Fuzz takes files containing LLVMFuzzerTestOneInput
+# and links them against libFuzzer, AFL++ and honggfuzz. The oss-fuzz
+# build script can be found at https://github.com/google/oss-fuzz/tree/master/projects/elfutils
+#
+# The testsuite links them against fuzz-main.c (which is a local driver reading
+# files into buffers and passing those buffers to LLVMFuzzerTestOneInput).
+#
+# And various fuzzing engines working with binaries reading files
+# can be used as well because fuzz-main.c provides what they expect.
+noinst_HEADERS=fuzz.h
+fuzz_dwfl_core_SOURCES = fuzz-main.c fuzz-dwfl-core.c
+fuzz_dwfl_core_LDADD = $(libelf) $(libdw)
+
+fuzz_libdwfl_SOURCES = fuzz-main.c fuzz-libdwfl.c
+fuzz_libdwfl_LDADD = $(libelf) $(libdw)
+
+fuzz_libelf_SOURCES = fuzz-main.c fuzz-libelf.c
+fuzz_libelf_LDADD = $(libelf) $(libdw)
+
 # We want to test the libelf header against the system elf.h header.
 # Don't include any -I CPPFLAGS. Except when we install our own elf.h.
 if !INSTALL_ELFH
diff --git a/tests/fuzz-dwfl-core-crashes/empty b/tests/fuzz-dwfl-core-crashes/empty
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fuzz-dwfl-core-crashes/oss-fuzz-41566 b/tests/fuzz-dwfl-core-crashes/oss-fuzz-41566
new file mode 100644
index 0000000000000000000000000000000000000000..a4181e6572fea9d568e787b0a4b57bf5defe4563
GIT binary patch
literal 1553
zcmb<-^>JfjWK@8F|4<r1IWRx~LIlYA|Np-L14M)cC=Fr>fEZ9VL<T}KGT{)1sfFv4
zUU)AQVivOgk-<g|cVzZp@;xxpL4YbwB4i4>JUX8$ju<rrO9()6D<mHh!)664a$sa&
zU<1<5{z0w`Xl5eE95NeC848yfYVm&sNSOdFA{Z>7;vhRg07Vl897w_@)^+$TB4i4>
TJctkTAH-MG^A~=X;*bRZ0NBqd

literal 0
HcmV?d00001

diff --git a/tests/fuzz-dwfl-core-crashes/oss-fuzz-41570 b/tests/fuzz-dwfl-core-crashes/oss-fuzz-41570
new file mode 100644
index 0000000000000000000000000000000000000000..4052572f5a484963b7bf03bb350368877671e7b1
GIT binary patch
literal 1233
zcmb<-^>JfjWK_Tf7#Sb{Ro;R@j6vZ)5TFXvi#KpK)60jW_Kb!A{ty6VOppp_{$x;q
U@IXR1zy$|x8Hg$z3WkL+07HkLIsgCw

literal 0
HcmV?d00001

diff --git a/tests/fuzz-dwfl-core-crashes/oss-fuzz-41572 b/tests/fuzz-dwfl-core-crashes/oss-fuzz-41572
new file mode 100644
index 0000000000000000000000000000000000000000..534d611b0c606003019d026f1028f248d2f1d656
GIT binary patch
literal 4872
zcmeHJUrSU$6hE42VVcHLra{*S=^y*YEb$@YMy(<UEk=+Kv}Ir$W{aEPoAwa&G1hOe
z&q3u!@DubL^wML{!*<Wi?{}RUE!ylw+<|+~pE<vK&YU@O&ukRuraL=2griGt%dR96
zbHbsqOvzcuU7!QU9Rr8m>2m~!RUZZ$K^cu~eR=2@41f=);*6^T<_iIipV$Xy{1MmR
zI&Np_m`EwW1%3pw#@Vg0#QI7``R{Qi&XR>mG}e1z1H2OYD|K3><Im)!Y}Hb|<l~^0
zMrIGr_#-a5wLh+ItWy7JjS#mT?~h}=PMn^<od7qEKntJ_PV?Chwa6XcAMHs4oNNPF
z2ykWSNgbStGr~&i_k4e_OZUUU+4=;o5#ZoXWbghNe+A;EeSgGlf^qzLxOm3L{UJ_U
zpYT^|5my4P+#;?FT=h(*>D4v!L2B2j)@FUx<m~C#9`<pu(e9ozCog6n8o<ejkBfjK
z1x_)#H1&J;{(Mobdi90j^3*N89h6V!MM$X+Y5jDlX-$stuZtdKytN=>(u2=6wu*iY
zKtI%DYya|s#SejEN2G~ybKL%`CV)#CgyHD-8ualy`@J8)LH%1oKbqtB$)h{N9Lqdl
z-LYZ5CX@Owr=*LkPirq=sw9^;n#fTdh}GCOlV^5j_5ow#o-_UfIrs;##uO*c^}I5s
z(eyjEu6fLxKfis}DRZ`!!Nj2Q$#`yw$-T0%>ATH7bH);R3iIky2iMNp68r~G*y;3r
z5*%xkINd`Xrz|mhsw}SsoacX?@;TU(B*)8)g~zsbhat1}FGBvlVdr_&DzL|NN_2fM
zlh}%Lv(wBebI6<t?<C<JgK})oNm>_>c}HMusnh?>@GereaisnrOV#SJ1(Mz+!n*-w
z^!_o%$-4l0dzA}iuSf6xKz-WF?SCQFvsa_{exyBnYS<$wH*622xj>eJ?Kz{2lRXbT
z><ablY3NZ`q0hg0RkgnjX^CB{t>%|2&+_Y2lTRjZWS1)|YwOvil{Hl^X0PWa#&fmT
hlK0IROPjVc(9Xco%Rq8BVsAToi*K*Eoq@xVfnU&q=l%cy

literal 0
HcmV?d00001

diff --git a/tests/fuzz-dwfl-core.c b/tests/fuzz-dwfl-core.c
new file mode 100644
index 00000000..79b1db07
--- /dev/null
+++ b/tests/fuzz-dwfl-core.c
@@ -0,0 +1,58 @@
+#include <assert.h>
+#include <config.h>
+#include <stdlib.h>
+#include ELFUTILS_HEADER(dwfl)
+#include "fuzz.h"
+#include "system.h"
+
+/* This fuzz target was initially used to fuzz systemd and
+   there elfutils is hidden behind functions receiving file
+   names and file descriptors. To cover that code the fuzz
+   target converts bytes it receives into temporary files
+   and passes their file descriptors to elf_begin instead of calling
+   something like elf_memory (which can process bytes directly).
+   New fuzzers covering elfutils should avoid this pattern.  */
+
+static const Dwfl_Callbacks core_callbacks =
+  {
+    .find_elf = dwfl_build_id_find_elf,
+    .find_debuginfo = dwfl_standard_find_debuginfo,
+  };
+
+int
+LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
+{
+  char fname[] = "/tmp/fuzz-dwfl-core.XXXXXX";
+  int fd = -1;
+  off_t offset;
+  ssize_t n;
+  Elf *core = NULL;
+  Dwfl *dwfl = NULL;
+
+  fd = mkstemp (fname);
+  assert (fd >= 0);
+
+  n = write_retry (fd, data, size);
+  assert (n >= 0);
+
+  offset = lseek (fd, 0, SEEK_SET);
+  assert (offset == 0);
+
+  elf_version (EV_CURRENT);
+  core = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+  if (core == NULL)
+    goto cleanup;
+  dwfl = dwfl_begin (&core_callbacks);
+  assert(dwfl != NULL);
+  if (dwfl_core_file_report (dwfl, core, NULL) < 0)
+    goto cleanup;
+  if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+    goto cleanup;
+
+cleanup:
+  dwfl_end (dwfl);
+  elf_end (core);
+  close (fd);
+  unlink (fname);
+  return 0;
+}
diff --git a/tests/fuzz-libdwfl-crashes/oss-fuzz-45629 b/tests/fuzz-libdwfl-crashes/oss-fuzz-45629
new file mode 100644
index 00000000..90f7abb8
--- /dev/null
+++ b/tests/fuzz-libdwfl-crashes/oss-fuzz-45629
@@ -0,0 +1,2 @@
+!<arch>
+                                                ÿ         `
diff --git a/tests/fuzz-libdwfl-crashes/oss-fuzz-45634 b/tests/fuzz-libdwfl-crashes/oss-fuzz-45634
new file mode 100644
index 0000000000000000000000000000000000000000..88c175379aa26d2fa8e7989ed385e9cd73cfa1c9
GIT binary patch
literal 140
vcmY$iNi0gvu;Wr75tx~oL3Jc>)w}w*F)=a{G=YNw0^kb&D=089z<CM)vX~Q5

literal 0
HcmV?d00001

diff --git a/tests/fuzz-libdwfl-crashes/oss-fuzz-45635 b/tests/fuzz-libdwfl-crashes/oss-fuzz-45635
new file mode 100644
index 0000000000000000000000000000000000000000..3990be403767a7f75b77add057109aa36e3983fc
GIT binary patch
literal 129
zcmY$iNi0gvu;bD<4)t+kVq#=u0D=l2r~zUS%cDX8#QpUD>wl2Ie~>%}hyVfskW2zs
zy(>`be?_24dU@&P|Nr0r4+eT5(0J4%`mS>V)3J8LU70<zkF`lx@6t6hU}TJDIHcnb
GGy?$5Xf5{u

literal 0
HcmV?d00001

diff --git a/tests/fuzz-libdwfl-crashes/oss-fuzz-45646 b/tests/fuzz-libdwfl-crashes/oss-fuzz-45646
new file mode 100644
index 00000000..ccddd0a6
--- /dev/null
+++ b/tests/fuzz-libdwfl-crashes/oss-fuzz-45646
@@ -0,0 +1 @@
+                                    ÿÿ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        Uª                                                                                 
\ No newline at end of file
diff --git a/tests/fuzz-libdwfl.c b/tests/fuzz-libdwfl.c
new file mode 100644
index 00000000..02e15c03
--- /dev/null
+++ b/tests/fuzz-libdwfl.c
@@ -0,0 +1,50 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "libdwfl.h"
+#include "system.h"
+
+static const char *debuginfo_path = "";
+static const Dwfl_Callbacks cb =
+  {
+    NULL,
+    dwfl_standard_find_debuginfo,
+    NULL,
+    (char **)&debuginfo_path,
+  };
+
+
+int
+LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
+{
+  char fname[] = "/tmp/fuzz-libdwfl.XXXXXX";
+  int fd;
+  ssize_t n;
+
+  fd = mkstemp (fname);
+  assert (fd >= 0);
+
+  n = write_retry (fd, data, size);
+  assert (n == (ssize_t) size);
+
+  close (fd);
+
+  Dwarf_Addr bias = 0;
+  Dwfl *dwfl = dwfl_begin (&cb);
+  dwfl_report_begin (dwfl);
+
+  Dwfl_Module *mod = dwfl_report_offline (dwfl, fname, fname, -1);
+  dwfl_module_getdwarf (mod, &bias);
+
+  dwfl_end (dwfl);
+  unlink (fname);
+  return 0;
+}
diff --git a/tests/fuzz-libelf-crashes/oss-fuzz-45637 b/tests/fuzz-libelf-crashes/oss-fuzz-45637
new file mode 100644
index 0000000000000000000000000000000000000000..32677f29323462f501c3848d676ccecc6fd5b36d
GIT binary patch
literal 684
zcmcgqF>=B%5Ii|{K~e-JFQ5peCK*$vfC3j8KY$_yynq=>8r;&-x#Iz;(k1x;|Dg+9
z_H^f1ke;29bhlccRwu6~W#JM>KvyOp*^eEd3FZ{x!ynV5tgbWA^{y5mdd3)!0uTWQ
zIvP$}(}1;->#wpvCIZn4m|2d?xI+n6J^D#@Vq|K{dnNfXoF5#-GP=1tzDs$Kq0$d@
z;&t<>ved^|Mc=-De@HVN^<YU|{CAr-cF(S{U_LRUFOE`FoMBWLZC68r=IYvinJ<mW
zhq!5F33)$gLhDLN@daSL*}re4Mp<#o3YxZUzVIg!eedSg+hW%}SIwR~2bT}=S5>-6
KlQ*K>E&T$DpnIMG

literal 0
HcmV?d00001

diff --git a/tests/fuzz-libelf-crashes/oss-fuzz-45682 b/tests/fuzz-libelf-crashes/oss-fuzz-45682
new file mode 100644
index 0000000000000000000000000000000000000000..7ff59b5789cc49448fb2cdb88cdc89536137b6a1
GIT binary patch
literal 321
zcmZur+YNvq3@oULV{`(yz%O_4-yat@)~lo-l}pGyI7-j7tsw>`qLBe`R5ri|)5<i{
zRJ^c=n&p7VvfOpV+wJ7P$QQk@587X`>|kB!gmdL?T@D%TBDc*@5193A<34#CJ+#s|
A6aWAK

literal 0
HcmV?d00001

diff --git a/tests/fuzz-libelf.c b/tests/fuzz-libelf.c
new file mode 100644
index 00000000..bdb51fe8
--- /dev/null
+++ b/tests/fuzz-libelf.c
@@ -0,0 +1,89 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "system.h"
+
+void
+fuzz_logic_one (char *fname, int compression_type)
+{
+  (void) elf_version (EV_CURRENT);
+  int fd = open (fname, O_RDONLY);
+  Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
+  if (elf != NULL) {
+    size_t strndx;
+    elf_getshdrstrndx (elf, &strndx);
+
+    Elf_Scn *scn = NULL;
+    // Iterate through sections
+    while ((scn = elf_nextscn (elf, scn)) != NULL) {
+      GElf_Shdr mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &mem);
+      const char *name = elf_strptr (elf, strndx, shdr->sh_name);
+
+      // Two options for reading sections. We keep the code structure
+      // so it resembles the test code.
+      // Compress and get data of the section
+      if ((shdr->sh_flags & SHF_COMPRESSED) != 0) {
+        if (elf_compress (scn, compression_type, 0) >= 0) {
+          elf_getdata (scn, NULL);
+        }
+      } else if (name != NULL) {
+        if (name[0] == '.' && name[1] == 'z') {
+          if (elf_compress_gnu (scn, 0, 0) >= 0) {
+            elf_getdata (scn, NULL);
+          }
+        }
+      }
+    }
+    elf_end (elf);
+  }
+  close (fd);
+}
+
+void
+fuzz_logic_twice (char *fname, int open_flags, Elf_Cmd cmd)
+{
+  (void) elf_version (EV_CURRENT);
+  int fd = open (fname, open_flags);
+  Elf *elf = elf_begin (fd, cmd, NULL);
+  if (elf != NULL) {
+    size_t elf_size = 0;
+    elf_rawfile (elf, &elf_size);
+    elf_end (elf);
+  }
+  close (fd);
+}
+
+int
+LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
+{
+  char fname[] = "/tmp/fuzz-libelf.XXXXXX";
+  int fd;
+  ssize_t n;
+
+  fd = mkstemp (fname);
+  assert (fd >= 0);
+
+  n = write_retry (fd, data, size);
+  assert (n == (ssize_t) size);
+
+  close (fd);
+
+  fuzz_logic_one (fname, 0);
+  fuzz_logic_one (fname, 1);
+  fuzz_logic_twice (fname, O_RDONLY, ELF_C_READ);
+  fuzz_logic_twice (fname, O_RDONLY | O_WRONLY, ELF_C_RDWR);
+  fuzz_logic_twice (fname, O_RDONLY, ELF_C_READ_MMAP);
+  fuzz_logic_twice (fname, O_RDONLY | O_WRONLY, ELF_C_RDWR_MMAP);
+
+  unlink (fname);
+  return 0;
+}
diff --git a/tests/fuzz-main.c b/tests/fuzz-main.c
new file mode 100644
index 00000000..35573792
--- /dev/null
+++ b/tests/fuzz-main.c
@@ -0,0 +1,43 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "fuzz.h"
+
+int
+main (int argc, char **argv)
+{
+  for (int i = 1; i < argc; i++)
+    {
+      fprintf (stderr, "Running: %s\n", argv[i]);
+
+      FILE *f = fopen (argv[i], "r");
+      assert (f);
+
+      int p = fseek (f, 0, SEEK_END);
+      assert (p >= 0);
+
+      long len = ftell (f);
+      assert (len >= 0);
+
+      p = fseek (f, 0, SEEK_SET);
+      assert (p >= 0);
+
+      void *buf = malloc (len);
+      assert (buf != NULL || len == 0);
+
+      size_t n_read = fread (buf, 1, len, f);
+      assert (n_read == (size_t) len);
+
+      (void) fclose (f);
+
+      int r = LLVMFuzzerTestOneInput (buf, len);
+
+      /* Non-zero return values are reserved by LibFuzzer for future use
+         https://llvm.org/docs/LibFuzzer.html#fuzz-target  */
+      assert (r == 0);
+
+      free (buf);
+
+      fprintf (stderr, "Done:    %s: (%zd bytes)\n", argv[i], n_read);
+    }
+}
diff --git a/tests/fuzz.h b/tests/fuzz.h
new file mode 100644
index 00000000..c8fe7a3a
--- /dev/null
+++ b/tests/fuzz.h
@@ -0,0 +1,9 @@
+#ifndef _FUZZ_H
+#define _FUZZ_H	1
+
+#include <stddef.h>
+#include <stdint.h>
+
+int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size);
+
+#endif /* fuzz.h */
diff --git a/tests/run-fuzz-dwfl-core.sh b/tests/run-fuzz-dwfl-core.sh
new file mode 100755
index 00000000..7af0a23c
--- /dev/null
+++ b/tests/run-fuzz-dwfl-core.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec $srcdir/run-fuzzer.sh fuzz-dwfl-core
diff --git a/tests/run-fuzz-libdwfl.sh b/tests/run-fuzz-libdwfl.sh
new file mode 100755
index 00000000..1453e2e0
--- /dev/null
+++ b/tests/run-fuzz-libdwfl.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec $srcdir/run-fuzzer.sh fuzz-libdwfl
diff --git a/tests/run-fuzz-libelf.sh b/tests/run-fuzz-libelf.sh
new file mode 100755
index 00000000..1de1c791
--- /dev/null
+++ b/tests/run-fuzz-libelf.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec $srcdir/run-fuzzer.sh fuzz-libelf
diff --git a/tests/run-fuzzer.sh b/tests/run-fuzzer.sh
new file mode 100755
index 00000000..4a3270d8
--- /dev/null
+++ b/tests/run-fuzzer.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+fuzzer="$1"
+
+. $srcdir/test-subr.sh
+
+# Valgrind is turned off because hongfuzz and AFL keep track of
+# processes and signals they receive and valgrind shouldn't
+# interfer with that. Apart from that it reports memory leaks
+# in timeout we aren't interested in:
+#==53620== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
+#==53620==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
+#==53620==    by 0x4860959: timer_create@@GLIBC_2.3.3 (timer_create.c:59)
+#==53620==    by 0x10AFCD: ??? (in /usr/bin/timeout)
+#==53620==    by 0x10AC18: ??? (in /usr/bin/timeout)
+#==53620==    by 0x48B00B2: (below main) (libc-start.c:308)
+#==53620==
+unset VALGRIND_CMD
+
+# honggfuzz sets ASAN and UBSAN options compatible with it
+# so they are reset early to prevent the environment from
+# affecting the test
+unset ASAN_OPTIONS
+unset UBSAN_OPTIONS
+
+fuzz_time=${FUZZ_TIME:-180}
+timeout=30
+
+# run_one is used to process files without honggfuzz
+# to get backtraces that otherwise can be borked in honggfuzz runs
+# so it has to set ASAN and UBSAN options itself
+run_one()
+{
+    testrun timeout -s9 $timeout env \
+        ASAN_OPTIONS=allocator_may_return_null=1 \
+        UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 \
+        ${abs_builddir}/${fuzzer} "$1"
+}
+
+# Here the fuzz target processes files one by one to be able
+# to catch memory leaks and other issues that can't be discovered
+# with honggfuzz.
+find ${abs_srcdir}/${fuzzer}-crashes/ -type f | while read file; do
+    run_one $file || { echo "*** failure in $file"; printf "$file\n" >>CRASHES; }
+done
+
+if [ -n "$honggfuzz" ]; then
+    tempfiles log
+
+    testrun $honggfuzz --run_time $fuzz_time -n 1 -v --exit_upon_crash \
+            -i ${abs_srcdir}/${fuzzer}-crashes/ \
+            -t $timeout --tmout_sigvtalrm \
+            -o OUT \
+            --logfile log \
+            -- ${abs_builddir}/${fuzzer} ___FILE___
+
+    rm -rf OUT
+
+    # hongfuzz always exits successfully so to tell "success" and "failure" apart
+    # it's necessary to look for reports it leaves when processes it monitors crash.
+    # Eventually it will be possible to pass --exit_code_upon_crash, which combined
+    # with --exit_upon_crash can be used to get honggfuzz to fail, but it hasn't been
+    # released yet. Initially it was used but on machines with the latest stable release
+    # tests that should have failed passed, which led to https://github.com/google/honggfuzz/pull/432
+    if [ -f HONGGFUZZ.REPORT.TXT ]; then
+        tail -n 25 log
+        cat HF.sanitizer.log* || true
+        cat HONGGFUZZ.REPORT.TXT
+        for crash in $(sed -n 's/^FUZZ_FNAME: *//p' HONGGFUZZ.REPORT.TXT); do
+            run_one $crash || { printf "$crash\n" >>CRASHES; }
+        done
+    fi
+fi
+
+if [ -n "$afl_fuzz" ]; then
+    # ASAN_OPTIONS and UBSAN_OPTIONS are compatible with both AFL and AFL++. They were borrowed from
+    # https://github.com/AFLplusplus/AFLplusplus/blob/74a8f145e09d0361d8f576eb3f2e8881b6116f18/src/afl-forkserver.c#L523
+    common_san_opts="abort_on_error=1:malloc_context_size=0:symbolize=0:allocator_may_return_null=1"
+    handle_san_opts="handle_segv=0:handle_sigbus=0:handle_abort=0:handle_sigfpe=0:handle_sigill=0"
+
+    testrun timeout --preserve-status $fuzz_time \
+	    env AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 \
+	        ASAN_OPTIONS="$common_san_opts:$handle_san_opts:detect_leaks=0:detect_odr_violation=0" \
+	        UBSAN_OPTIONS="$common_san_opts:$handle_san_opts:halt_on_error=1" \
+	    $afl_fuzz -i ${abs_srcdir}/${fuzzer}-crashes/ \
+	    -t $(expr $timeout '*' 1000) \
+	    -m none \
+	    -o OUT \
+	    -- ${abs_builddir}/${fuzzer} @@
+
+    find OUT/crashes OUT/hangs -type f | while read file; do
+        run_one $file || { printf "$file\n" >>CRASHES; }
+    done
+
+    [ -f CRASHES ] || rm -rf OUT
+fi
+
+if [ -f CRASHES ]; then
+    printf "Files triggering various crashes:\n"
+    cat CRASHES
+    exit 1
+fi
+
+exit 0
-- 
2.35.1


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-03-22  0:33 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-21 11:21 [PATCH] tests: integrate fuzz-targets into the test suite Evgeny Vereshchagin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).