-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests: integrate fuzz-targets into the test suite
[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 google/honggfuzz#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 #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 google/honggfuzz#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 google/oss-fuzz#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 #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 <[email protected]>
- Loading branch information
Showing
22 changed files
with
460 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
!<arch> | ||
� ` |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
�� U� |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.