]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
tests: add a fuzzer for mnt_table_parse_stream
authorEvgeny Vereshchagin <evvers@ya.ru>
Sat, 11 Apr 2020 13:25:26 +0000 (13:25 +0000)
committerEvgeny Vereshchagin <evvers@ya.ru>
Thu, 6 Aug 2020 12:33:11 +0000 (12:33 +0000)
The fuzzer is supposed to cover `mnt_table_parse_stream`, which is
used by systemd to parse /proc/self/mountinfo. The systemd project
has run into memory leaks there at least twice:

https://github.com/systemd/systemd/pull/12252#issuecomment-482804040
https://github.com/systemd/systemd/issues/8504

so it seems to be a good idea to continuously fuzz that particular
function.

The patch can be tested locally by installing clang and running
./tools/oss-fuzz.sh. Currently the fuzzer is failing with
```
=================================================================
==96638==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 216 byte(s) in 1 object(s) allocated from:
    #0 0x50cd77 in calloc (/home/vagrant/util-linux/out/test_mount_fuzz+0x50cd77)
    #1 0x58716a in mnt_new_fs /home/vagrant/util-linux/libmount/src/fs.c:36:25
    #2 0x54f224 in __table_parse_stream /home/vagrant/util-linux/libmount/src/tab_parse.c:728:9
    #3 0x54eed8 in mnt_table_parse_stream /home/vagrant/util-linux/libmount/src/tab_parse.c:804:8
    #4 0x5448b2 in LLVMFuzzerTestOneInput /home/vagrant/util-linux/libmount/src/fuzz.c:19:16
    #5 0x44cc88 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/vagrant/util-linux/out/test_mount_fuzz+0x44cc88)
    #6 0x44d8b0 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) (/home/vagrant/util-linux/out/test_mount_fuzz+0x44d8b0)
    #7 0x44e270 in fuzzer::Fuzzer::MutateAndTestOne() (/home/vagrant/util-linux/out/test_mount_fuzz+0x44e270)
    #8 0x450617 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/home/vagrant/util-linux/out/test_mount_fuzz+0x450617)
    #9 0x43adbb in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/vagrant/util-linux/out/test_mount_fuzz+0x43adbb)
    #10 0x42ad46 in main (/home/vagrant/util-linux/out/test_mount_fuzz+0x42ad46)
    #11 0x7fa084f621a2 in __libc_start_main (/lib64/libc.so.6+0x271a2)

SUMMARY: AddressSanitizer: 216 byte(s) leaked in 1 allocation(s).
INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.
```

Once the bug is fixed and the OSS-Fuzz counterpart is merged it should be possible
to turn on CIFuzz to make sure the fuzz target can be built and run for some time
without crashing: https://google.github.io/oss-fuzz/getting-started/continuous-integration/

Signed-off-by: Evgeny Vereshchagin <evvers@ya.ru>
configure.ac
libmount/src/Makemodule.am
libmount/src/fuzz.c [new file with mode: 0644]
tools/oss-fuzz.sh [new file with mode: 0755]

index 97f404f929b5ee109814c866761629c0b2735bc4..8815d270b8b06a78705f8aa000f5de10222fccc9 100644 (file)
@@ -189,6 +189,13 @@ AS_IF([test "x$enable_ubsan" = xyes], [
 ])
 AC_SUBST([UBSAN_LDFLAGS])
 
+AC_ARG_ENABLE([fuzzing-engine],
+  AS_HELP_STRING([--enable-fuzzing-engine], [compile with fuzzing engine]),
+  [], [enable_fuzzing_engine=no]
+)
+AC_PROG_CXX
+AM_CONDITIONAL([FUZZING_ENGINE], [test x$enable_fuzzing_engine = xyes])
+
 dnl libtool-2
 LT_INIT
 
index eaa69c135ed5a629df002eae351e38619913719a..32fdd3f24b1c9721ff387b68e8b54af59a07665c 100644 (file)
@@ -154,6 +154,19 @@ test_mount_debug_CFLAGS = $(libmount_tests_cflags)
 test_mount_debug_LDFLAGS = $(libmount_tests_ldflags)
 test_mount_debug_LDADD = $(libmount_tests_ldadd)
 
+if FUZZING_ENGINE
+check_PROGRAMS += test_mount_fuzz
+
+test_mount_fuzz_SOURCES = libmount/src/fuzz.c
+
+# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements
+nodist_EXTRA_test_mount_fuzz_SOURCES = dummy.cxx
+
+test_mount_fuzz_CFLAGS = $(libmount_tests_cflags)
+test_mount_fuzz_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_fuzz_LDADD = $(libmount_tests_ldadd) $(LIB_FUZZING_ENGINE)
+endif
+
 endif # BUILD_LIBMOUNT_TESTS
 
 
diff --git a/libmount/src/fuzz.c b/libmount/src/fuzz.c
new file mode 100644 (file)
index 0000000..7363e2b
--- /dev/null
@@ -0,0 +1,25 @@
+#include "mountP.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        struct libmnt_table *tb = NULL;
+        FILE *f = NULL;
+
+        if (size == 0)
+                return 0;
+
+        tb = mnt_new_table();
+        assert(tb);
+
+        f = fmemopen((char*) data, size, "re");
+        assert(f);
+
+        (void) mnt_table_parse_stream(tb, f, "mountinfo");
+
+        mnt_unref_table(tb);
+        fclose(f);
+
+        return 0;
+}
diff --git a/tools/oss-fuzz.sh b/tools/oss-fuzz.sh
new file mode 100755 (executable)
index 0000000..60aeb3b
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -ex
+
+export LC_CTYPE=C.UTF-8
+
+export CC=${CC:-clang}
+export CXX=${CXX:-clang++}
+export LIB_FUZZING_ENGINE=${LIB_FUZZING_ENGINE:--fsanitize=fuzzer}
+
+SANITIZER=${SANITIZER:-address -fsanitize-address-use-after-scope}
+flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
+
+export CFLAGS=${CFLAGS:-$flags}
+export CXXFLAGS=${CXXFLAGS:-$flags}
+
+export OUT=${OUT:-$(pwd)/out}
+mkdir -p $OUT
+
+./autogen.sh
+./configure --disable-all-programs --enable-fuzzing-engine --enable-libmount --enable-libblkid
+make -j$(nproc) V=1 check-programs
+
+find . -maxdepth 1 -type f -executable -name "test_*_fuzz" -exec mv {} $OUT \;
+find . -type f -name "fuzz-*.dict" -exec cp {} $OUT \;