erofs-utils: fsck: add a preliminary fuzzer
authorGao Xiang <hsiangkao@linux.alibaba.com>
Mon, 5 Jun 2023 17:05:51 +0000 (01:05 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Wed, 7 Jun 2023 13:44:11 +0000 (21:44 +0800)
Let's introduce a fuzzer for fsck.erofs by using libFuzzer [1]:
 - Clang 6.0+ installed;
 - Build with "CC=clang ./configure --enable-fuzzing";
 - Set stack size by using `ulimit -s` properly;
 - fsck/fuzz_erofsfsck -timeout=40 -max_total_time=600 CORPUS_DIR.

[1] https://www.llvm.org/docs/LibFuzzer.html
[ Gao Xiang: a dedicated test job is also set up at
   https://github.com/erofs/erofsnightly/actions/workflows/fuzzers.yml ]
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Link: https://lore.kernel.org/r/20230605170551.273399-1-xiang@kernel.org
configure.ac
fsck/Makefile.am
fsck/main.c

index 2ade490d4aafd1a73854624607943a50075064ca..cd6be4a582ccc5e077b00b0b6ad708cf04190030 100644 (file)
@@ -29,6 +29,41 @@ else
   AC_MSG_ERROR([pkg-config is required. See pkg-config.freedesktop.org])
 fi
 
+dnl Check if the flag is supported by compiler
+dnl CC_CHECK_CFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [
+  AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]),
+    [ac_save_CFLAGS="$CFLAGS"
+     CFLAGS="$CFLAGS $1"
+     AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 0; }])],
+       [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"],
+       [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"])
+     CFLAGS="$ac_save_CFLAGS"
+    ])
+
+  AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
+    [$2], [$3])
+])
+
+dnl Check if the flag is supported by compiler (cacheable)
+dnl CC_CHECK_CFLAG([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+AC_DEFUN([CC_CHECK_CFLAG], [
+  AC_CACHE_CHECK([if $CC supports $1 flag],
+    AS_TR_SH([cc_cv_cflags_$1]),
+    CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here!
+  )
+
+  AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
+    [$2], [$3])
+])
+
+dnl CC_CHECK_CFLAGS([FLAG1 FLAG2], [action-if-found], [action-if-not])
+AC_DEFUN([CC_CHECK_CFLAGS], [
+  for flag in $1; do
+    CC_CHECK_CFLAG($flag, [$2], [$3])
+  done
+])
+
 dnl EROFS_UTILS_PARSE_DIRECTORY
 dnl Input:  $1 = a string to a relative or absolute directory
 dnl Output: $2 = the variable to set with the absolute directory
@@ -73,6 +108,12 @@ AC_ARG_ENABLE([werror],
     [enable_werror="$enableval"],
     [enable_werror="no"])
 
+AC_ARG_ENABLE([fuzzing],
+    [AS_HELP_STRING([--enable-fuzzing],
+                    [set up fuzzing mode @<:@default=no@:>@])],
+    [enable_fuzzing="$enableval"],
+    [enable_fuzzing="no"])
+
 AC_ARG_ENABLE(lz4,
    [AS_HELP_STRING([--disable-lz4], [disable LZ4 compression support @<:@default=enabled@:>@])],
    [enable_lz4="$enableval"], [enable_lz4="yes"])
@@ -356,6 +397,16 @@ fi
 # Enable 64-bit off_t
 CFLAGS+=" -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
 
+# Configure fuzzing mode
+AS_IF([test "x$enable_fuzzing" != "xyes"], [], [
+  CC_CHECK_CFLAGS(["-fsanitize=address,fuzzer-no-link"], [
+    CFLAGS="$CFLAGS -g -O1 -fsanitize=address,fuzzer-no-link"
+  ], [
+    AC_MSG_ERROR([Compiler doesn't support `-fsanitize=address,fuzzer-no-link`])
+  ])
+])
+AM_CONDITIONAL([ENABLE_FUZZING], [test "x${enable_fuzzing}" = "xyes"])
+
 # Set up needed symbols, conditionals and compiler/linker flags
 AM_CONDITIONAL([ENABLE_LZ4], [test "x${have_lz4}" = "xyes"])
 AM_CONDITIONAL([ENABLE_LZ4HC], [test "x${have_lz4hc}" = "xyes"])
index e6a1fb6b7740459fa65fb034e1ad7533806f45bd..9366a9dd2d3d1d436463ef3659c968d8ccef1f0d 100644 (file)
@@ -8,3 +8,12 @@ fsck_erofs_SOURCES = main.c
 fsck_erofs_CFLAGS = -Wall -I$(top_srcdir)/include
 fsck_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} \
        ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS}
+
+if ENABLE_FUZZING
+noinst_PROGRAMS   = fuzz_erofsfsck
+fuzz_erofsfsck_SOURCES = main.c
+fuzz_erofsfsck_CFLAGS = -Wall -I$(top_srcdir)/include -DFUZZING
+fuzz_erofsfsck_LDFLAGS = -fsanitize=address,fuzzer
+fuzz_erofsfsck_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} \
+       ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS}
+endif
index 47c01d8c26d703b31fade3dcaf94e0c1fc9b3212..85df8a666eb9155cac0006a153710560e1234331 100644 (file)
@@ -263,10 +263,11 @@ static void erofsfsck_set_attributes(struct erofs_inode *inode, char *path)
 
 static int erofs_check_sb_chksum(void)
 {
-       int ret;
+#ifndef FUZZING
        u8 buf[EROFS_MAX_BLOCK_SIZE];
        u32 crc;
        struct erofs_super_block *sb;
+       int ret;
 
        ret = blk_read(0, buf, 0, 1);
        if (ret) {
@@ -285,6 +286,7 @@ static int erofs_check_sb_chksum(void)
                fsckcfg.corrupted = true;
                return -1;
        }
+#endif
        return 0;
 }
 
@@ -792,7 +794,11 @@ out:
        return ret;
 }
 
-int main(int argc, char **argv)
+#ifdef FUZZING
+int erofsfsck_fuzz_one(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
 {
        int err;
 
@@ -819,6 +825,10 @@ int main(int argc, char **argv)
                goto exit;
        }
 
+#ifdef FUZZING
+       cfg.c_dbg_lvl = -1;
+#endif
+
        err = dev_open_ro(cfg.c_img_path);
        if (err) {
                erofs_err("failed to open image file");
@@ -875,3 +885,28 @@ exit:
        erofs_exit_configure();
        return err ? 1 : 0;
 }
+
+#ifdef FUZZING
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
+{
+       int fd, ret;
+       char filename[] = "/tmp/erofsfsck_libfuzzer_XXXXXX";
+       char *argv[] = {
+               "fsck.erofs",
+               "--extract",
+               filename,
+       };
+
+       fd = mkstemp(filename);
+       if (fd < 0)
+               return -errno;
+       if (write(fd, Data, Size) != Size) {
+               close(fd);
+               return -EIO;
+       }
+       close(fd);
+       ret = erofsfsck_fuzz_one(ARRAY_SIZE(argv), argv);
+       unlink(filename);
+       return ret ? -1 : 0;
+}
+#endif