From eabe24901f4235e215d17b9162524d76765a2161 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Thu, 30 Mar 2023 00:21:17 +0200 Subject: [PATCH] Fuzzing: compile the whole libespeak with -fsanitize=fuzzer-no-link -fsanitize=fuzzer-no-link makes it workable to build a library with fuzzing enabled, and let the fuzzing test program explicitly trigger the fuzzing. This allows fuzzing to trace cmp instructions to guide the fuzzing to improve results. --- .github/workflows/fuzzing.yml | 7 ++++--- Makefile.am | 11 ++++------- configure.ac | 33 ++++++++++++++++++++++----------- tests/fuzzing/synth_fuzzer.c | 10 +++++++--- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index 8b00b6d2..0c60b579 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -16,6 +16,7 @@ jobs: include: - arch: x86-32 archdeps: "gcc-multilib g++-multilib libpcaudio-dev:i386 libsonic-dev:i386 libc6-dbg:i386" + archconfig: "--host=i686-linux-gnu" archconfigflags: "-m32" - arch: x86-64 @@ -31,11 +32,11 @@ jobs: - name: autoconf run: ./autogen.sh ; chmod -x INSTALL m4/*.m4 - name: configure - run: CC=clang CXX=clang++ - CFLAGS="${{ matrix.archconfigflags }} -fsanitize=address,undefined -fstack-protector-strong -g -Og -fno-omit-frame-pointer" + run: CC="clang ${{ matrix.archconfigflags }}" CXX="clang++ ${{ matrix.archconfigflags }}" + CFLAGS="-fsanitize=address,undefined -fstack-protector-strong -g -Og -fno-omit-frame-pointer" CXXFLAGS="${{ matrix.archconfigflags }} -fsanitize=address,undefined -fstack-protector-strong -g -Og -fno-omit-frame-pointer" LDFLAGS="-fsanitize=address,undefined -lubsan" - ./configure --with-libfuzzer + ./configure ${{ matrix.archconfig }} --with-libfuzzer - name: Store the fuzzer config if: ${{ failure() }} uses: actions/upload-artifact@v3 diff --git a/Makefile.am b/Makefile.am index 4cb10391..59554689 100644 --- a/Makefile.am +++ b/Makefile.am @@ -262,7 +262,7 @@ man1_MANS += src/speak-ng.1 endif src_speak_ng_LDADD = src/libespeak-ng.la -src_speak_ng_LDFLAGS = -static -lm ${PCAUDIOLIB_LIBS} +src_speak_ng_LDFLAGS = -static -lm ${PCAUDIOLIB_LIBS} ${LIBFUZZER_NO_MAIN} src_speak_ng_CFLAGS = -Isrc/libespeak-ng ${AM_CFLAGS} bin_PROGRAMS += src/espeak-ng @@ -271,7 +271,7 @@ if HAVE_RONN man1_MANS += src/espeak-ng.1 endif -src_espeak_ng_LDADD = src/libespeak-ng.la ${PCAUDIOLIB_LIBS} +src_espeak_ng_LDADD = src/libespeak-ng.la ${PCAUDIOLIB_LIBS} ${LIBFUZZER_NO_MAIN} src_espeak_ng_SOURCES = src/espeak-ng.c if HAVE_LIBFUZZER nodist_EXTRA_src_espeak_ng_SOURCES = force-cxx-linking.cxx @@ -354,14 +354,11 @@ endif if HAVE_LIBFUZZER -tests_ssml_fuzzer_test_CFLAGS += -fsanitize=fuzzer -tests_ssml_fuzzer_test_LDFLAGS = -fsanitize=fuzzer - noinst_PROGRAMS = tests/fuzzing/synth_fuzzer.test tests_fuzzing_synth_fuzzer_test_SOURCES = tests/fuzzing/synth_fuzzer.c tests_fuzzing_synth_fuzzer_test_LDADD = src/libespeak-ng.la -tests_fuzzing_synth_fuzzer_test_CFLAGS= ${AM_CFLAGS} -fsanitize=fuzzer -DPATH_ESPEAK_DATA=\"$(abs_top_srcdir)/espeak-ng-data\" -Isrc/libespeak-ng -tests_fuzzing_synth_fuzzer_test_LDFLAGS= ${AM_LDFLAGS} -fsanitize=fuzzer -static -lm -Wl,-z,relro ${PCAUDIOLIB_LIBS} +tests_fuzzing_synth_fuzzer_test_CFLAGS= ${AM_CFLAGS} -DPATH_ESPEAK_DATA=\"$(abs_top_srcdir)/espeak-ng-data\" -Isrc/libespeak-ng +tests_fuzzing_synth_fuzzer_test_LDFLAGS= ${AM_LDFLAGS} -static -lm -Wl,-z,relro ${PCAUDIOLIB_LIBS} ${LIBFUZZER_NO_MAIN} else tests_ssml_fuzzer_test_LDADD += tests/libfuzzrunner.la endif diff --git a/configure.ac b/configure.ac index 1359545f..cf8284e4 100644 --- a/configure.ac +++ b/configure.ac @@ -136,17 +136,28 @@ else fi -AC_LANG_PUSH(C) -TEMP_CFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -fsanitize=fuzzer" -AC_MSG_CHECKING([if $CC supports fuzzer with the -fsanitize=fuzzer flag]) -AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( [[]], [[]])], - [have_fuzzer_fuzzer=yes], - [have_fuzzer_fuzzer=no]) -AC_MSG_RESULT($have_fuzzer_fuzzer) -CFLAGS="$TEMP_CFLAGS" -AC_LANG_POP(C) +if test "$with_libfuzzer" = yes ; then + AC_LANG_PUSH(C) + TEMP_CFLAGS="$CFLAGS" + TEMP_LIBS="$LIBS" + HOST_CPU="`$CC -print-multiarch | sed -e 's/-.*//'`" + LIBFUZZER_NO_MAIN="`$CC -print-runtime-dir`/libclang_rt.fuzzer_no_main-$HOST_CPU.a -lstdc++ -lm" + CFLAGS="$CFLAGS -fsanitize=fuzzer-no-link" + LIBS="$LIBS $LIBFUZZER_NO_MAIN" + AC_MSG_CHECKING([if $CC supports fuzzer with the -fsanitize=fuzzer flag]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( [[]], [[]])], + [have_fuzzer_fuzzer=yes], + [have_fuzzer_fuzzer=no]) + AC_MSG_RESULT($have_fuzzer_fuzzer) + if test "$have_fuzzer_fuzzer" != yes ; then + CFLAGS="$TEMP_CFLAGS" + LIBFUZZER_NO_MAIN="" + fi + AC_SUBST(LIBFUZZER_NO_MAIN) + LIBS="$TEMP_LIBS" + AC_LANG_POP(C) +fi dnl ================================================================ dnl FreeBSD check. diff --git a/tests/fuzzing/synth_fuzzer.c b/tests/fuzzing/synth_fuzzer.c index ca3dddac..3cf0007d 100644 --- a/tests/fuzzing/synth_fuzzer.c +++ b/tests/fuzzing/synth_fuzzer.c @@ -40,10 +40,10 @@ espeak_callback(short *data, int samples, espeak_EVENT *events) } /* See http://llvm.org/docs/LibFuzzer.html */ -extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); - -extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern int LLVMFuzzerRunDriver(int *argc, char ***argv, + int (*UserCb)(const uint8_t *Data, size_t Size)); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { int buflength = size+1; if (!initialized) @@ -78,3 +78,7 @@ extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) return 0; } + +int main(int argc, char *argv[]) { + LLVMFuzzerRunDriver(&argc, &argv, LLVMFuzzerTestOneInput); +}