build/fuzzing: integrate fuzz targets into the build system
authorMatthew Waters <matthew@centricular.com>
Wed, 6 Apr 2022 09:22:44 +0000 (19:22 +1000)
committerMatthew Waters <matthew@centricular.com>
Wed, 6 Apr 2022 22:17:35 +0000 (08:17 +1000)
Currently disabled but may be enabled later.

Updates the existing fuzzing to use shared libraries as that's easier
for meson to deal with if there is a mix of static and shared libraries
on the system.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2123>

ci/fuzzing/README.txt
ci/fuzzing/build-oss-fuzz.sh
ci/fuzzing/gst-discoverer.c
ci/fuzzing/gst-discoverer.corpus [new file with mode: 0644]
ci/fuzzing/meson.build [new file with mode: 0644]
ci/fuzzing/typefind.c
meson.build
meson_options.txt

index c9621a0455b66ac5b7229d0350efd97227d93c32..5ee4b8d0d0ea0ebc5c720a3e0abd3a11df3b11ad 100644 (file)
@@ -32,6 +32,12 @@ Fuzzing GStreamer
   function whose signature follows the LibFuzzer signature:
   https://llvm.org/docs/LibFuzzer.html
 
+* *.corpus
+
+  A file matching a test name that contains a list of files to use when
+  starting a fuzzing run.  Providing an initial set files can speed up
+  the fuzzing process significantly.
+
 * TODO
 
   * Add a standalone build script
index cb4801c5ccd11fe80c3916b82792f8a4137a732f..475f7c62ff190e135417936625db4a875a0399bd 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash -eu
+#!/bin/bash -eux
 
 # build-oss-fuzz.sh
 #
 # /!\ Do not override any CC, CXX, CFLAGS, ... variables
 #
 
-# This script is divided in two parts
-#
-# 1) Build all the dependencies statically
-#
-# 2) Build the fuzzing targets
+rm -rf $WORK/*
+rm -rf $OUT/lib $OUT/*_seed_corpus.zip
 
 # Prefix where we will temporarily install everything
 PREFIX=$WORK/prefix
@@ -30,176 +27,108 @@ export PATH=$PREFIX/bin:$PATH
 # Minimize gst-debug level/code
 export CFLAGS="$CFLAGS -DGST_LEVEL_MAX=2"
 
-#
 echo "CFLAGS : " $CFLAGS
 echo "CXXFLAGS : " $CXXFLAGS
-PLUGIN_DIR=$PREFIX/lib/gstreamer-1.0
-
-rm -rf $WORK/*
 
 # Switch to work directory
 cd $WORK
 
-# 1) BUILD GLIB AND GSTREAMER
-# Note: we build glib ourselves so that we get proper malloc/free backtraces
-tar xvJf $SRC/glib-2.72.0.tar.xz
-cd glib-2.72.0
-# options taken from glib's oss-fuzz build definition
+mkdir -p $OUT/lib/gstreamer-1.0
+
+# build ogg
+pushd $SRC/ogg
+./autogen.sh
+./configure --prefix="$PREFIX" --libdir="$PREFIX/lib"
+make clean
+make -j$(nproc)
+make install
+popd
+
+# build vorbis
+pushd $SRC/vorbis
+./autogen.sh
+./configure --prefix="$PREFIX" --libdir="$PREFIX/lib"
+make clean
+make -j$(nproc)
+make install
+popd
+
+# build theora
+pushd $SRC/theora
+./autogen.sh
+./configure --prefix="$PREFIX" --libdir="$PREFIX/lib"
+make clean
+make -j$(nproc)
+make install
+popd
+
+# Note: We don't use/build orc since it still seems to be problematic
+# with clang and the various sanitizers.
+
+# For now we only build core and base. Add other modules when/if needed
 meson \
     --prefix=$PREFIX \
     --libdir=lib \
-    --default-library=static \
+    --default-library=shared \
+    --force-fallback-for=zlib \
     -Db_lundef=false \
     -Doss_fuzz=enabled \
-    -Dlibmount=disabled \
-    _builddir
+    -Dglib:oss_fuzz=enabled \
+    -Dglib:libmount=disabled \
+    -Dglib:tests=false \
+    -Ddoc=disabled \
+    -Dexamples=disabled \
+    -Dintrospection=disabled \
+    -Dgood=disabled \
+    -Dugly=disabled \
+    -Dbad=disabled \
+    -Dlibav=disabled \
+    -Dges=disabled \
+    -Domx=disabled \
+    -Dvaapi=disabled \
+    -Dsharp=disabled \
+    -Drs=disabled \
+    -Dpython=disabled \
+    -Dlibnice=disabled \
+    -Ddevtools=disabled \
+    -Drtsp_server=disabled \
+    -Dgst-examples=disabled \
+    -Dqt5=disabled \
+    -Dorc=disabled \
+    -Dgtk_doc=disabled \
+    -Dgstreamer:tracer_hooks=false \
+    -Dgst-plugins-base:opus=disabled \
+    -Dgst-plugins-base:pango=disabled \
+    _builddir \
+    $SRC/gstreamer
 ninja -C _builddir
 ninja -C _builddir install
-cd ..
-
-# Note: We don't use/build orc since it still seems to be problematic
-# with clang and the various sanitizers.
 
-# For now we only build core and base. Add other modules when/if needed
-for i in gstreamer;
+# copy out the fuzzing binaries
+for BINARY in $(find _builddir/ci/fuzzing -type f -executable -print)
 do
-    mkdir -p $i
-    cd $i
-    meson \
-        --prefix=$PREFIX \
-        --libdir=lib \
-        --default-library=static \
-        -Db_lundef=false \
-        -Ddoc=disabled \
-        -Dexamples=disabled \
-        -Dintrospection=disabled \
-        -Dgood=disabled \
-        -Dugly=disabled \
-        -Dbad=disabled \
-        -Dlibav=disabled \
-        -Dges=disabled \
-        -Domx=disabled \
-        -Dvaapi=disabled \
-        -Dsharp=disabled \
-        -Drs=disabled \
-        -Dpython=disabled \
-        -Dlibnice=disabled \
-        -Ddevtools=disabled \
-        -Drtsp_server=disabled \
-        -Dgst-examples=disabled \
-        -Dqt5=disabled \
-        -Dorc=disabled \
-        -Dgtk_doc=disabled \
-        -Dgstreamer:tracer_hooks=false \
-        -Dgstreamer:registry=false \
-        -Dgst-plugins-base:opus=disabled \
-        -Dgst-plugins-base:pango=disabled \
-        _builddir \
-        $SRC/$i
-    ninja -C _builddir
-    ninja -C _builddir install
-    cd ..
+  BASENAME=${BINARY##*/}
+  rm -rf "$OUT/$BASENAME*"
+  cp $BINARY $OUT/$BASENAME
+  patchelf --set-rpath '$ORIGIN/lib' $OUT/$BASENAME
 done
 
+# copy any relevant corpus
+for CORPUS in $(find "$SRC/gstreamer/ci/fuzzing" -type f -name "*.corpus"); do
+  BASENAME=${CORPUS##*/}
+  pushd "$SRC/gstreamer"
+  zip $OUT/${BASENAME%%.*}_seed_corpus.zip . -ws -r -i@$CORPUS
+  popd
+done
 
+# copy dependant libraries
+find "$PREFIX/lib" -maxdepth 1 -type f -name "*.so*" -exec cp -d "{}" $OUT/lib \; -print
+# add rpath that point to the correct place to all shared libraries
+find "$OUT/lib" -maxdepth 1 -type f -name "*.so*" -exec patchelf --debug --set-rpath '$ORIGIN' {} \;
+find "$PREFIX/lib" -maxdepth 1 -type l -name "*.so*" -exec cp -d "{}" $OUT/lib \; -print
 
-# 2) Build the target fuzzers
-
-# All targets will be linked in with $LIB_FUZZING_ENGINE which contains the
-# actual fuzzing runner. Anything fuzzing engine can be used provided it calls
-# the same function as libfuzzer.
-
-# Note: The fuzzer .o needs to be first compiled with CC and then linked with CXX
-
-# We want to statically link everything, except for shared libraries
-# that are present on the base image. Those need to be specified
-# beforehand and explicitely linked dynamically If any of the static
-# dependencies require a pre-installed shared library, you need to add
-# that library to the following list
-PREDEPS_LDFLAGS="-Wl,-Bdynamic -ldl -lm -pthread -lrt -lpthread"
-
-# These are the basic .pc dependencies required to build any of the fuzzing targets
-# That is : glib, gstreamer core and gst-app
-# The extra target-specific dependencies are to be specified later
-COMMON_DEPS="glib-2.0 gio-2.0 gstreamer-1.0 gstreamer-app-1.0"
-
-# For each target, defined the following:
-# TARGET_DEPS : Extra .pc dependencies for the target (in addition to $COMMON_DEPS)
-#               All dependencies (including sub-dependencies) must be speecified
-# PLUGINS : .a of the plugins to link
-#           They must match the static plugins declared/registered in the target
-
-#
-# TARGET : push-based ogg/theora/vorbis discoverer
-#
-# FIXME : Rename to discoverer_push_oggtheoravorbis
-
-TARGET_DEPS=" gstreamer-pbutils-1.0 \
-             gstreamer-video-1.0 \
-             gstreamer-audio-1.0 \
-             gstreamer-riff-1.0 \
-             gstreamer-tag-1.0 \
-             zlib ogg vorbis vorbisenc \
-             theoraenc theoradec theora cairo"
-
-PLUGINS="$PLUGIN_DIR/libgstcoreelements.a \
-       $PLUGIN_DIR/libgsttypefindfunctions.a \
-       $PLUGIN_DIR/libgstplayback.a \
-       $PLUGIN_DIR/libgstapp.a \
-       $PLUGIN_DIR/libgstvorbis.a \
-       $PLUGIN_DIR/libgsttheora.a \
-       $PLUGIN_DIR/libgstogg.a"
-
-echo
-echo ">>>> BUILDING gst-discoverer"
-echo
-BUILD_CFLAGS="$CFLAGS `pkg-config --static --cflags $COMMON_DEPS $TARGET_DEPS`"
-BUILD_LDFLAGS="-Wl,-static `pkg-config --static --libs $COMMON_DEPS $TARGET_DEPS`"
-
-$CC $CFLAGS $BUILD_CFLAGS -c $SRC/gstreamer/ci/fuzzing/gst-discoverer.c -o $SRC/gstreamer/ci/fuzzing/gst-discoverer.o
-$CXX $CXXFLAGS \
-      -o $OUT/gst-discoverer \
-      $PREDEPS_LDFLAGS \
-      $SRC/gstreamer/ci/fuzzing/gst-discoverer.o \
-      $PLUGINS \
-      $BUILD_LDFLAGS \
-      $LIB_FUZZING_ENGINE \
-      -Wl,-Bdynamic
-
-#
-# TARGET : push-based typefind
-#
+find "$PREFIX/lib/gstreamer-1.0" -maxdepth 1 -type f -name "*.so" -exec cp -d "{}" $OUT/lib/gstreamer-1.0 \;
+find "$OUT/lib/gstreamer-1.0" -type f -name "*.so*" -exec patchelf --debug --set-rpath '$ORIGIN/..' {} \;
 
-# typefindfunction depends on pbutils which depends on gst{audio|video|tag}
-TARGET_DEPS=" gstreamer-pbutils-1.0 \
-             gstreamer-video-1.0 \
-             gstreamer-audio-1.0 \
-             gstreamer-tag-1.0"
-
-PLUGINS="$PLUGIN_DIR/libgstcoreelements.a \
-       $PLUGIN_DIR/libgsttypefindfunctions.a \
-       $PLUGIN_DIR/libgstapp.a"
-
-echo
-echo ">>>> BUILDING typefind"
-echo
-BUILD_CFLAGS="$CFLAGS `pkg-config --static --cflags $COMMON_DEPS $TARGET_DEPS`"
-BUILD_LDFLAGS="-Wl,-static `pkg-config --static --libs $COMMON_DEPS $TARGET_DEPS`"
-
-$CC $CFLAGS $BUILD_CFLAGS -c $SRC/gstreamer/ci/fuzzing/typefind.c -o $SRC/gstreamer/ci/fuzzing/typefind.o
-$CXX $CXXFLAGS \
-      -o $OUT/typefind \
-      $PREDEPS_LDFLAGS \
-      $SRC/gstreamer/ci/fuzzing/typefind.o \
-      $PLUGINS \
-      $BUILD_LDFLAGS \
-      $LIB_FUZZING_ENGINE \
-      -Wl,-Bdynamic
-
-echo
-echo ">>>> Installing seed corpus"
-echo
-# FIXME : Sadly we apparently need to have the corpus downloaded in the
-#         Dockerfile and not here.
-
-cp $SRC/*_seed_corpus.zip $OUT
+# make it easier to spot dependency issues
+find "$OUT/lib/gstreamer-1.0" -maxdepth 1 -type f -name "*.so" -print -exec ldd {} \;
index 0440606a4a08b544385643ff923d75094dad6cae..8faf9011800be58c2143eca6928616b031199be7 100644 (file)
 #include <gst/gst.h>
 #include <gst/pbutils/pbutils.h>
 
-#ifndef LOCAL_FUZZ_BUILD
-GST_PLUGIN_STATIC_DECLARE (coreelements);
-GST_PLUGIN_STATIC_DECLARE (playback);
-GST_PLUGIN_STATIC_DECLARE (typefindfunctions);
-GST_PLUGIN_STATIC_DECLARE (app);
-GST_PLUGIN_STATIC_DECLARE (ogg);
-GST_PLUGIN_STATIC_DECLARE (theora);
-GST_PLUGIN_STATIC_DECLARE (vorbis);
-#endif
-
 /* push-based discoverer fuzzing target
  *
  * This application can be compiled with libFuzzer to simulate
@@ -99,17 +89,6 @@ LLVMFuzzerTestOneInput (const guint8 * data, size_t size)
 
     /* Only initialize and register plugins once */
     gst_init (NULL, NULL);
-
-#ifndef LOCAL_FUZZ_BUILD
-    GST_PLUGIN_STATIC_REGISTER (coreelements);
-    GST_PLUGIN_STATIC_REGISTER (playback);
-    GST_PLUGIN_STATIC_REGISTER (typefindfunctions);
-    GST_PLUGIN_STATIC_REGISTER (app);
-    GST_PLUGIN_STATIC_REGISTER (ogg);
-    GST_PLUGIN_STATIC_REGISTER (theora);
-    GST_PLUGIN_STATIC_REGISTER (vorbis);
-#endif
-
     initialized = TRUE;
   }
 
diff --git a/ci/fuzzing/gst-discoverer.corpus b/ci/fuzzing/gst-discoverer.corpus
new file mode 100644 (file)
index 0000000..29bab96
--- /dev/null
@@ -0,0 +1,4 @@
+subprojects/gst-integration-testsuites/medias/defaults/ogg/numerated_frames_blue.ogv
+subprojects/gst-integration-testsuites/medias/defaults/ogg/opus.1.ogg
+subprojects/gst-integration-testsuites/medias/defaults/ogg/vorbis_theora.0.ogg
+subprojects/gst-integration-testsuites/medias/defaults/ogg/vorbis_theora.1.ogg
diff --git a/ci/fuzzing/meson.build b/ci/fuzzing/meson.build
new file mode 100644 (file)
index 0000000..0b4e301
--- /dev/null
@@ -0,0 +1,40 @@
+if get_option('oss_fuzz').disabled()
+  subdir_done()
+endif
+
+fuzz_targets = [
+  ['gst-discoverer.c', false, ['gstreamer-pbutils-1.0']],
+  ['typefind.c'],
+]
+
+extra_sources = []
+gst_dep = dependency('gstreamer-1.0')
+common_deps = [gst_dep]
+
+cxx = meson.get_compiler('cpp')
+fuzzing_engine = cxx.find_library('FuzzingEngine', required: false)
+if fuzzing_engine.found()
+  common_deps += fuzzing_engine
+else
+  extra_sources += ['localfuzzer.c']
+endif
+
+foreach target : fuzz_targets
+  file_name = target.get(0)
+  test_name = file_name.split('.').get(0)
+
+  extra_deps = []
+  if target.length() >= 3
+    extra_deps = dependency(target.get(2))
+  endif
+
+  skip_test = false
+  if target.length() >= 2
+    skip_test = target.get(1)
+  endif
+  if not skip_test
+    exe = executable(test_name, [extra_sources, file_name],
+      dependencies: common_deps + extra_deps,
+    )
+  endif
+endforeach
index 999ba337fcb70adabe66926e74aa9d00cd2eb34e..f13fa38045e1bf20c98c2206229d14f20e235c44 100644 (file)
 #include <glib.h>
 #include <gst/gst.h>
 
-#ifndef LOCAL_FUZZ_BUILD
-GST_PLUGIN_STATIC_DECLARE (coreelements);
-GST_PLUGIN_STATIC_DECLARE (typefindfunctions);
-GST_PLUGIN_STATIC_DECLARE (app);
-#endif
-
 /* push-based typefind fuzzing target
  *
  * This application can be compiled with libFuzzer to simulate
@@ -58,7 +52,6 @@ custom_logger (const gchar * log_domain,
 int
 LLVMFuzzerTestOneInput (const guint8 * data, size_t size)
 {
-  GError *err = NULL;
   static gboolean initialized = FALSE;
   GstElement *pipeline, *source, *typefind, *fakesink;
   GstBuffer *buf;
@@ -73,12 +66,6 @@ LLVMFuzzerTestOneInput (const guint8 * data, size_t size)
     /* Only initialize and register plugins once */
     gst_init (NULL, NULL);
 
-#ifndef LOCAL_FUZZ_BUILD
-    GST_PLUGIN_STATIC_REGISTER (coreelements);
-    GST_PLUGIN_STATIC_REGISTER (typefindfunctions);
-    GST_PLUGIN_STATIC_REGISTER (app);
-#endif
-
     initialized = TRUE;
   }
 
index 579fd5038ed81e7245e2f8588f5f8aee533162ff..c65934ac7fc0dc6ec1c3a1cb9ad992aed5fa7cbc 100644 (file)
@@ -436,6 +436,8 @@ devenv_cmd = [setenv, '--builddir=@0@'.format(meson.global_build_root()),
               '--srcdir=@0@'.format(meson.global_source_root())]
 
 subdir('tests')
+subdir('ci/fuzzing')
+
 if meson.can_run_host_binaries() and build_machine.system() == 'linux' and host_machine.system() == 'windows'
   # FIXME: Ideally we could get the wrapper directly from meson
   devenv_cmd += ['--wine', host_machine.cpu_family() == 'x86_64' ? 'wine64' : 'wine32']
index 1f837ca9c6d36b20d4e0c18df60dbda3658d7d91..f077a19379b9605c7f1edf7af2969dd3a453b513 100644 (file)
@@ -18,6 +18,10 @@ option('tls', type : 'feature', value : 'auto', description : 'TLS support using
 option('qt5', type : 'feature', value : 'auto', description : 'Qt5 Support')
 option('tools', type : 'feature', value : 'auto', yield : true, description : 'Build command line tools')
 
+# Build for fuzzing
+option('oss_fuzz', type : 'feature', value : 'disabled',
+       description: 'Use fuzzing build environment')
+
 # Other options
 option('custom_subprojects', type : 'string', value : '', description : 'Comma-separated project names')
 option('gst-full-libraries', type : 'array', value : [],