From 3ea72c286e704ce14049eca04cb37c3b58ea0a02 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Thu, 28 Feb 2019 12:28:09 +0100 Subject: [PATCH] improve fuzz targets and oss-fuzz integration It replaces current fuzz setup with 5 new fuzzers integrating them with top-level Makefile. It now supports multiple fuzzing engines and Google's oss-fuzz. By default, the fuzzers are linked against an standalone runner to easily reproduce bugs. --- Makefile.am | 3 +- configure.ac | 8 ++++ fuzz/Makefile.am | 20 +++++++++ fuzz/StandaloneFuzzTargetMain.c | 46 +++++++++++++++++++++ fuzz/jpegsave_buffer_fuzzer.cc | 30 ++++++++++++++ fuzz/pngsave_buffer_fuzzer.cc | 30 ++++++++++++++ fuzz/sharpen_fuzzer.cc | 31 ++++++++++++++ fuzz/thumbnail_fuzzer.cc | 42 +++++++++++++++++++ fuzz/webpsave_buffer_fuzzer.cc | 30 ++++++++++++++ libvips/fuzz/Makefile.am | 6 --- libvips/fuzz/fuzz_new_from_buffer.c | 64 ----------------------------- 11 files changed, 239 insertions(+), 71 deletions(-) create mode 100644 fuzz/Makefile.am create mode 100644 fuzz/StandaloneFuzzTargetMain.c create mode 100644 fuzz/jpegsave_buffer_fuzzer.cc create mode 100644 fuzz/pngsave_buffer_fuzzer.cc create mode 100644 fuzz/sharpen_fuzzer.cc create mode 100644 fuzz/thumbnail_fuzzer.cc create mode 100644 fuzz/webpsave_buffer_fuzzer.cc delete mode 100644 libvips/fuzz/Makefile.am delete mode 100644 libvips/fuzz/fuzz_new_from_buffer.c diff --git a/Makefile.am b/Makefile.am index d46b7fbc..96b39889 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,7 +5,8 @@ SUBDIRS = \ po \ man \ doc \ - test + test \ + fuzz EXTRA_DIST = \ m4 \ diff --git a/configure.ac b/configure.ac index 638c9016..a1290259 100644 --- a/configure.ac +++ b/configure.ac @@ -1388,6 +1388,13 @@ if test x"$with_libexif" != x"no"; then CPPFLAGS="$save_CPPFLAGS" fi +# fuzzing +AC_ARG_VAR([LIB_FUZZING_ENGINE], + [fuzzing library, e.g. /path/to/libFuzzer.a]) +if test x"$LIB_FUZZING_ENGINE" = x; then + LIB_FUZZING_ENGINE="libstandaloneengine.a" +fi + # Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS # sort includes to get longer, more specific dirs first # helps, for example, selecting graphicsmagick over imagemagick @@ -1452,6 +1459,7 @@ AC_OUTPUT([ doc/Makefile doc/libvips-docs.xml po/Makefile.in + fuzz/Makefile ]) AC_MSG_RESULT([dnl diff --git a/fuzz/Makefile.am b/fuzz/Makefile.am new file mode 100644 index 00000000..8ad6d5cf --- /dev/null +++ b/fuzz/Makefile.am @@ -0,0 +1,20 @@ +FUZZPROGS = \ + jpegsave_buffer_fuzzer \ + pngsave_buffer_fuzzer \ + webpsave_buffer_fuzzer \ + sharpen_fuzzer \ + thumbnail_fuzzer + +AM_DEFAULT_SOURCE_EXT = .cc + +FUZZLIBS = libstandaloneengine.a + +# Include debug symbols by default as recommended by libfuzzer +AM_CXXFLAGS = -g -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = ${top_builddir}/libvips/libvips.la @VIPS_LIBS@ @LIB_FUZZING_ENGINE@ + +libstandaloneengine_a_SOURCES = StandaloneFuzzTargetMain.c + +noinst_PROGRAMS = $(FUZZPROGS) +noinst_LIBRARIES = $(FUZZLIBS) diff --git a/fuzz/StandaloneFuzzTargetMain.c b/fuzz/StandaloneFuzzTargetMain.c new file mode 100644 index 00000000..d2a1eb9b --- /dev/null +++ b/fuzz/StandaloneFuzzTargetMain.c @@ -0,0 +1,46 @@ +/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This main() function can be linked to a fuzz target (i.e. a library +// that exports LLVMFuzzerTestOneInput() and possibly LLVMFuzzerInitialize()) +// instead of libFuzzer. This main() function will not perform any fuzzing +// but will simply feed all input files one by one to the fuzz target. +// +// Use this file to provide reproducers for bugs when linking against libFuzzer +// or other fuzzing engine is undesirable. +//===----------------------------------------------------------------------===*/ +#include +#include +#include +#include + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); +extern int LLVMFuzzerInitialize(int *argc, char ***argv); +int main(int argc, char **argv) { + const char *progname; + if ((progname = strrchr(argv[0], '/'))) + progname++; + else + progname = argv[0]; + fprintf(stderr, "%s: running %d inputs\n", progname, argc - 1); + LLVMFuzzerInitialize(&argc, &argv); + for (int i = 1; i < argc; i++) { + fprintf(stderr, "Running: %s\n", argv[i]); + FILE *f = fopen(argv[i], "r+"); + assert(f); + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char *buf = (unsigned char*)malloc(len); + size_t n_read = fread(buf, 1, len, f); + fclose(f); + assert(n_read == len); + LLVMFuzzerTestOneInput(buf, len); + free(buf); + fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read); + } +} diff --git a/fuzz/jpegsave_buffer_fuzzer.cc b/fuzz/jpegsave_buffer_fuzzer.cc new file mode 100644 index 00000000..491c2cd5 --- /dev/null +++ b/fuzz/jpegsave_buffer_fuzzer.cc @@ -0,0 +1,30 @@ +#include + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + VipsImage *image; + size_t len; + void *buf; + + if( !(image = vips_image_new_from_buffer( data, size, "", NULL )) ) { + return( 0 ); + } + + if( vips_jpegsave_buffer( image, &buf, &len, NULL ) ) { + g_object_unref( image ); + return( 0 ); + } + + g_free( buf ); + g_object_unref( image ); + + return( 0 ); +} diff --git a/fuzz/pngsave_buffer_fuzzer.cc b/fuzz/pngsave_buffer_fuzzer.cc new file mode 100644 index 00000000..35489d39 --- /dev/null +++ b/fuzz/pngsave_buffer_fuzzer.cc @@ -0,0 +1,30 @@ +#include + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + VipsImage *image; + void *buf; + size_t len; + + if( !(image = vips_image_new_from_buffer( data, size, "", NULL )) ) { + return( 0 ); + } + + if( vips_pngsave_buffer( image, &buf, &len, NULL ) ) { + g_object_unref( image ); + return( 0 ); + } + + g_free( buf ); + g_object_unref( image ); + + return( 0 ); +} diff --git a/fuzz/sharpen_fuzzer.cc b/fuzz/sharpen_fuzzer.cc new file mode 100644 index 00000000..c93d705e --- /dev/null +++ b/fuzz/sharpen_fuzzer.cc @@ -0,0 +1,31 @@ +#include + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + VipsImage *in, *out; + double d; + + if( !(in = vips_image_new_from_buffer( data, size, "", NULL )) ) { + return( 0 ); + } + + if( vips_sharpen( in, &out, NULL ) ) { + g_object_unref( in ); + return( 0 ); + } + + vips_avg( out, &d, NULL ); + + g_object_unref( out ); + g_object_unref( in ); + + return( 0 ); +} diff --git a/fuzz/thumbnail_fuzzer.cc b/fuzz/thumbnail_fuzzer.cc new file mode 100644 index 00000000..a3a744bf --- /dev/null +++ b/fuzz/thumbnail_fuzzer.cc @@ -0,0 +1,42 @@ +#include + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + VipsImage *in, *out; + size_t width, height; + double d; + + if( !(in = vips_image_new_from_buffer( data, size, "", NULL )) ) { + return( 0 ); + } + + width = in->Xsize; + height = in->Ysize; + + /* Skip big images. It is likely to timeout. + */ + if ( width * height > 256 * 256 ) { + g_object_unref( in ); + return( 0 ); + } + + if( vips_thumbnail_image( in, &out, 42, NULL ) ) { + g_object_unref( in ); + return( 0 ); + } + + vips_avg( out, &d, NULL ); + + g_object_unref( out ); + g_object_unref( in ); + + return( 0 ); +} diff --git a/fuzz/webpsave_buffer_fuzzer.cc b/fuzz/webpsave_buffer_fuzzer.cc new file mode 100644 index 00000000..86157c7f --- /dev/null +++ b/fuzz/webpsave_buffer_fuzzer.cc @@ -0,0 +1,30 @@ +#include + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + VipsImage *image; + void *buf; + size_t len; + + if( !(image = vips_image_new_from_buffer( data, size, "", NULL )) ) { + return( 0 ); + } + + if( vips_webpsave_buffer( image, &buf, &len, NULL ) ) { + g_object_unref( image ); + return( 0 ); + } + + g_free( buf ); + g_object_unref( image ); + + return( 0 ); +} diff --git a/libvips/fuzz/Makefile.am b/libvips/fuzz/Makefile.am deleted file mode 100644 index 17753242..00000000 --- a/libvips/fuzz/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LTLIBRARIES = libfuzz.la - -libfuzz_la_SOURCES = \ - fuzz_new_from_buffer.c - -AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/fuzz/fuzz_new_from_buffer.c b/libvips/fuzz/fuzz_new_from_buffer.c deleted file mode 100644 index 1063345e..00000000 --- a/libvips/fuzz/fuzz_new_from_buffer.c +++ /dev/null @@ -1,64 +0,0 @@ -/* fuzz targets for libfuzzer - * - * 28/7/17 - * - first attempt - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -/* -#define VIPS_DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include - -int -vips__fuzztarget_new_from_buffer( const guint8 *data, size_t size ) -{ - VipsImage *image; - double d; - - /* Have one for each format as well. - */ - if( !(image = vips_image_new_from_buffer( data, size, "", NULL )) ) - /* libfuzzer does not allow error return. - */ - return( 0 ); - - if( vips_avg( image, &d, NULL ) ) - return( 0 ); - - g_object_unref( image ); - - return( 0 ); -}