From e0f7e0ece557dd5ed02aa194367ec0ff09abdc5a Mon Sep 17 00:00:00 2001 From: Erik de Castro Lopo Date: Sun, 17 Nov 2019 16:15:51 +1100 Subject: oss-fuzz: Add fuzzing targets These fuzzing targets were originally from: https://github.com/guidovranken/flac-fuzzers.git but having them in a separate repo was a huge pain in the neck when working on fixing any problems found. --- .gitignore | 2 + Makefile.am | 2 +- configure.ac | 50 +++++-- oss-fuzz/Makefile.am | 62 ++++++++ oss-fuzz/fuzz-decoder.cc | 355 +++++++++++++++++++++++++++++++++++++++++++++ oss-fuzz/fuzz-encoder.cc | 154 ++++++++++++++++++++ oss-fuzz/fuzz-encoder.dict | 17 +++ 7 files changed, 630 insertions(+), 12 deletions(-) create mode 100644 oss-fuzz/Makefile.am create mode 100644 oss-fuzz/fuzz-decoder.cc create mode 100644 oss-fuzz/fuzz-encoder.cc create mode 100644 oss-fuzz/fuzz-encoder.dict diff --git a/.gitignore b/.gitignore index 6e9ffce1..c38747c3 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,5 @@ test/picture.diff test/picture.log .dirstamp microbench/benchmark_residual +oss-fuzz/fuzz-decoder +oss-fuzz/fuzz-encoder diff --git a/Makefile.am b/Makefile.am index 83e0140f..ce4519f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,7 +31,7 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = doc include m4 man src test build microbench +SUBDIRS = doc include m4 man src test build microbench oss-fuzz if EXAMPLES SUBDIRS += examples diff --git a/configure.ac b/configure.ac index 88f89a91..5dd4e3a9 100644 --- a/configure.ac +++ b/configure.ac @@ -374,6 +374,32 @@ AC_HELP_STRING([--disable-cpplibs], [Do not build libFLAC++]), esac], [disable_cpplibs=false]) AM_CONDITIONAL(FLaC__WITH_CPPLIBS, [test "x$disable_cpplibs" != xtrue]) +AC_ARG_ENABLE([oss-fuzzers], + [AS_HELP_STRING([--enable-oss-fuzzers], + [Whether to generate the fuzzers for OSS-Fuzz (Clang only)])], + [have_oss_fuzzers=yes], [have_oss_fuzzers=no]) + +if test "x$have_oss_fuzzers" = "xyes"; then + if test "x$xiph_cv_c_compiler_clang" = "xyes" ; then + AM_CONDITIONAL([USE_OSSFUZZERS], [test "x$have_oss_fuzzers" = "xyes"]) + if test "x$LIB_FUZZING_ENGINE" = "x" ; then + # Only set this if it is empty. + LIB_FUZZING_ENGINE=-fsanitize=fuzzer + fi + else + AM_CONDITIONAL([USE_OSSFUZZERS], [test "false" = "true"]) + # Disable fuzzer if the compiler is not Clang. + AC_MSG_WARN([*** Ozz-Fuzz is disabled because that requres the Clang compiler.]) + have_oss_fuzzers="no (compiler is GCC)" + fi +else + AM_CONDITIONAL([USE_OSSFUZZERS], [test "false" = "true"]) +fi + +AM_CONDITIONAL([USE_OSSFUZZ_FLAG], [test "x$LIB_FUZZING_ENGINE" = "x-fsanitize=fuzzer"]) +AM_CONDITIONAL([USE_OSSFUZZ_STATIC], [test -f "$LIB_FUZZING_ENGINE"]) +AC_SUBST([LIB_FUZZING_ENGINE]) + dnl check for ogg library AC_ARG_ENABLE([ogg], AC_HELP_STRING([--disable-ogg], [Disable ogg support (default: test for libogg)]), @@ -576,7 +602,8 @@ AC_CONFIG_FILES([ \ test/metaflac-test-files/Makefile \ test/pictures/Makefile \ build/Makefile \ - microbench/Makefile + microbench/Makefile \ + oss-fuzz/Makefile ]) AC_OUTPUT @@ -585,19 +612,20 @@ AC_MSG_RESULT([ Configuration summary : - FLAC version : ........................ ${VERSION} + FLAC version : ............................ ${VERSION} - Host CPU : ............................ ${host_cpu} - Host Vendor : ......................... ${host_vendor} - Host OS : ............................. ${host_os} + Host CPU : ................................ ${host_cpu} + Host Vendor : ............................. ${host_vendor} + Host OS : ................................. ${host_os} ]) - echo " Compiler is GCC : ..................... ${ac_cv_c_compiler_gnu}" + echo " Compiler is GCC : ......................... ${ac_cv_c_compiler_gnu}" if test x$ac_cv_c_compiler_gnu = xyes ; then - echo " GCC version : ......................... ${GCC_VERSION}" + echo " GCC version : ............................. ${GCC_VERSION}" fi - echo " Compiler is Clang : ................... ${xiph_cv_c_compiler_clang}" - echo " SSE optimizations : ................... ${sse_os}" - echo " Asm optimizations : ................... ${asm_optimisation}" - echo " Ogg/FLAC support : .................... ${have_ogg}" + echo " Compiler is Clang : ....................... ${xiph_cv_c_compiler_clang}" + echo " SSE optimizations : ....................... ${sse_os}" + echo " Asm optimizations : ....................... ${asm_optimisation}" + echo " Ogg/FLAC support : ........................ ${have_ogg}" + echo " Fuzzing support (Clang only) : ............ ${have_oss_fuzzers}" echo diff --git a/oss-fuzz/Makefile.am b/oss-fuzz/Makefile.am new file mode 100644 index 00000000..7c4e2f60 --- /dev/null +++ b/oss-fuzz/Makefile.am @@ -0,0 +1,62 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2019 Xiph.Org Foundation +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under different licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/oss-fuzz + +AM_CXXFLAGS = -std=c++11 + +EXTRA_DIST = \ + fuzz-encoder.dict \ + fuzzing/Readme.md \ + fuzzing/datasource/datasource.hpp \ + fuzzing/datasource/id.hpp \ + fuzzing/exception.hpp \ + fuzzing/memory.hpp \ + fuzzing/types.hpp + +if USE_OSSFUZZ_FLAG +FUZZ_FLAG = $(LIB_FUZZING_ENGINE) +FUZZ_LDADD = -lFuzzer +else +if USE_OSSFUZZ_STATIC +FUZZ_LDADD = $(LIB_FUZZING_ENGINE) +FUZZ_FLAG = -lFuzzer +endif +endif + +noinst_PROGRAMS = + +if USE_OSSFUZZERS +noinst_PROGRAMS += fuzz-decoder fuzz-encoder +endif + +fuzz_decoder_SOURCES = fuzz-decoder.cc +fuzz_decoder_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) +fuzz_decoder_LDFLAGS = $(AM_LDFLAGS) -static +fuzz_decoder_LDADD = $(flac_libs) $(FUZZ_LDADD) + +fuzz_encoder_SOURCES = fuzz-encoder.cc +fuzz_encoder_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) +fuzz_encoder_LDFLAGS = $(AM_LDFLAGS) -static +fuzz_encoder_LDADD = $(flac_libs) $(FUZZ_LDADD) + +flac_libs = \ + $(top_builddir)/src/libFLAC/libFLAC-static.la \ + $(top_builddir)/src/libFLAC++/libFLAC++-static.la \ + @OGG_LIBS@ \ + -lm + diff --git a/oss-fuzz/fuzz-decoder.cc b/oss-fuzz/fuzz-decoder.cc new file mode 100644 index 00000000..b2733874 --- /dev/null +++ b/oss-fuzz/fuzz-decoder.cc @@ -0,0 +1,355 @@ +#include +#include + +#include +#include + +#include "FLAC++/decoder.h" + +template <> FLAC__MetadataType fuzzing::datasource::Base::Get(const uint64_t id) { + (void)id; + switch ( Get() ) { + case 0: + return FLAC__METADATA_TYPE_STREAMINFO; + case 1: + return FLAC__METADATA_TYPE_PADDING; + case 2: + return FLAC__METADATA_TYPE_APPLICATION; + case 3: + return FLAC__METADATA_TYPE_SEEKTABLE; + case 4: + return FLAC__METADATA_TYPE_VORBIS_COMMENT; + case 5: + return FLAC__METADATA_TYPE_CUESHEET; + case 6: + return FLAC__METADATA_TYPE_PICTURE; + case 7: + return FLAC__METADATA_TYPE_UNDEFINED; + case 8: + return FLAC__MAX_METADATA_TYPE; + default: + return FLAC__METADATA_TYPE_STREAMINFO; + } +} + +namespace FLAC { + namespace Decoder { + class FuzzerStream : public Stream { + private: + fuzzing::datasource::Datasource& ds; + public: + FuzzerStream(fuzzing::datasource::Datasource& dsrc) : + Stream(), ds(dsrc) { } + + ::FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes) override { + try { + const size_t maxCopySize = *bytes; + + if ( maxCopySize > 0 ) { + /* memset just to test if this overwrites anything, and triggers ASAN */ + memset(buffer, 0, maxCopySize); + } + + const auto data = ds.GetData(0); + const auto dataSize = data.size(); + const auto copySize = std::min(maxCopySize, dataSize); + + if ( copySize > 0 ) { + memcpy(buffer, data.data(), copySize); + } + + *bytes = copySize; + + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } catch ( ... ) { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + } + + ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) override { + { + fuzzing::memory::memory_test(&(frame->header), sizeof(frame->header)); + fuzzing::memory::memory_test(&(frame->footer), sizeof(frame->footer)); + } + + { + const auto numChannels = get_channels(); + const size_t bytesPerChannel = frame->header.blocksize * sizeof(FLAC__int32); + for (size_t i = 0; i < numChannels; i++) { + fuzzing::memory::memory_test(buffer[i], bytesPerChannel); + } + } + + try { + if ( ds.Get() == true ) { + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } catch ( ... ) { } + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + + void error_callback(::FLAC__StreamDecoderErrorStatus status) override { + fuzzing::memory::memory_test(status); + } + + void metadata_callback(const ::FLAC__StreamMetadata *metadata) override { + fuzzing::memory::memory_test(metadata->type); + fuzzing::memory::memory_test(metadata->is_last); + fuzzing::memory::memory_test(metadata->length); + fuzzing::memory::memory_test(metadata->data); + } + + ::FLAC__StreamDecoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset) override { + fuzzing::memory::memory_test(absolute_byte_offset); + + try { + if ( ds.Get() == true ) { + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } else { + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + } catch ( ... ) { + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } + } +#if 0 + ::FLAC__StreamDecoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset) override { + fuzzing::memory::memory_test(*absolute_byte_offset); + + try { + if ( ds.Get() == true ) { + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } else { + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + } catch ( ... ) { + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } + } + + ::FLAC__StreamDecoderLengthStatus length_callback(FLAC__uint64 *stream_length) override { + fuzzing::memory::memory_test(*stream_length); + + try { + if ( ds.Get() == true ) { + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } else { + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } + } catch ( ... ) { + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } + } +#endif + }; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzzing::datasource::Datasource ds(data, size); + FLAC::Decoder::FuzzerStream decoder(ds); + + try { + { + ::FLAC__StreamDecoderInitStatus ret; + + if ( ds.Get() ) { + ret = decoder.init(); + } else { + ret = decoder.init_ogg(); + } + + if ( ret != FLAC__STREAM_DECODER_INIT_STATUS_OK ) { + goto end; + } + } + + if ( ds.Get() ) { +#ifdef FUZZER_DEBUG + printf("set_ogg_serial_number\n"); +#endif + decoder.set_ogg_serial_number(ds.Get()); + } + if ( ds.Get() ) { +#ifdef FUZZER_DEBUG + printf("set_md5_checking\n"); +#endif + decoder.set_md5_checking(ds.Get()); + } + if ( ds.Get() ) { +#ifdef FUZZER_DEBUG + printf("set_metadata_respond\n"); +#endif + decoder.set_metadata_respond(ds.Get<::FLAC__MetadataType>()); + } + if ( ds.Get() ) { + const auto idVector = ds.GetData(0); + unsigned char id[4]; + if ( idVector.size() >= sizeof(id) ) { + memcpy(id, idVector.data(), sizeof(id)); +#ifdef FUZZER_DEBUG + printf("set_metadata_respond_application\n"); +#endif + decoder.set_metadata_respond_application(id); + } + } + if ( ds.Get() ) { +#ifdef FUZZER_DEBUG + printf("set_metadata_respond_all\n"); +#endif + decoder.set_metadata_respond_all(); + } + if ( ds.Get() ) { +#ifdef FUZZER_DEBUG + printf("set_metadata_ignore\n"); +#endif + decoder.set_metadata_ignore(ds.Get<::FLAC__MetadataType>()); + } + if ( ds.Get() ) { + const auto idVector = ds.GetData(0); + unsigned char id[4]; + if ( idVector.size() >= sizeof(id) ) { + memcpy(id, idVector.data(), sizeof(id)); +#ifdef FUZZER_DEBUG + printf("set_metadata_ignore_application\n"); +#endif + decoder.set_metadata_ignore_application(id); + } + } + if ( ds.Get() ) { +#ifdef FUZZER_DEBUG + printf("set_metadata_ignore_all\n"); +#endif + decoder.set_metadata_ignore_all(); + } + + while ( ds.Get() ) { + switch ( ds.Get() ) { + case 0: + { +#ifdef FUZZER_DEBUG + printf("flush\n"); +#endif + const bool res = decoder.flush(); + fuzzing::memory::memory_test(res); + } + break; + case 1: + { +#ifdef FUZZER_DEBUG + printf("reset\n"); +#endif + const bool res = decoder.reset(); + fuzzing::memory::memory_test(res); + } + break; + case 2: + { +#ifdef FUZZER_DEBUG + printf("process_single\n"); +#endif + const bool res = decoder.process_single(); + fuzzing::memory::memory_test(res); + } + break; + case 3: + { +#ifdef FUZZER_DEBUG + printf("process_until_end_of_metadata\n"); +#endif + const bool res = decoder.process_until_end_of_metadata(); + fuzzing::memory::memory_test(res); + } + break; + case 4: + { +#ifdef FUZZER_DEBUG + printf("process_until_end_of_stream\n"); +#endif + const bool res = decoder.process_until_end_of_stream(); + fuzzing::memory::memory_test(res); + } + break; + case 5: + { +#ifdef FUZZER_DEBUG + printf("skip_single_frame\n"); +#endif + const bool res = decoder.skip_single_frame(); + fuzzing::memory::memory_test(res); + } + break; + case 6: + { +#ifdef FUZZER_DEBUG + printf("seek_absolute\n"); +#endif + const bool res = decoder.seek_absolute(ds.Get()); + fuzzing::memory::memory_test(res); + } + break; + case 7: + { +#ifdef FUZZER_DEBUG + printf("get_md5_checking\n"); +#endif + const bool res = decoder.get_md5_checking(); + fuzzing::memory::memory_test(res); + } + break; + case 8: + { +#ifdef FUZZER_DEBUG + printf("get_total_samples\n"); +#endif + const bool res = decoder.get_total_samples(); + fuzzing::memory::memory_test(res); + } + break; + case 9: + { +#ifdef FUZZER_DEBUG + printf("get_channels\n"); +#endif + const bool res = decoder.get_channels(); + fuzzing::memory::memory_test(res); + } + break; + case 10: + { +#ifdef FUZZER_DEBUG + printf("get_bits_per_sample\n"); +#endif + const bool res = decoder.get_bits_per_sample(); + fuzzing::memory::memory_test(res); + } + break; + case 11: + { +#ifdef FUZZER_DEBUG + printf("get_sample_rate\n"); +#endif + const bool res = decoder.get_sample_rate(); + fuzzing::memory::memory_test(res); + } + break; + case 12: + { +#ifdef FUZZER_DEBUG + printf("get_blocksize\n"); +#endif + const bool res = decoder.get_blocksize(); + fuzzing::memory::memory_test(res); + } + break; + } + } + } catch ( ... ) { } + +end: + { + const bool res = decoder.finish(); + fuzzing::memory::memory_test(res); + } + return 0; +} diff --git a/oss-fuzz/fuzz-encoder.cc b/oss-fuzz/fuzz-encoder.cc new file mode 100644 index 00000000..067af055 --- /dev/null +++ b/oss-fuzz/fuzz-encoder.cc @@ -0,0 +1,154 @@ +#include +#include +#include + +#include +#include + +#include "FLAC++/encoder.h" + +#define SAMPLE_VALUE_LIMIT (1024*1024*10) + +static_assert(SAMPLE_VALUE_LIMIT <= std::numeric_limits::max(), "Invalid SAMPLE_VALUE_LIMIT"); +static_assert(-SAMPLE_VALUE_LIMIT >= std::numeric_limits::min(), "Invalid SAMPLE_VALUE_LIMIT"); + +namespace FLAC { + namespace Encoder { + class FuzzerStream : public Stream { + private: + // fuzzing::datasource::Datasource& ds; + public: + FuzzerStream(fuzzing::datasource::Datasource&) : + Stream() { } + + ::FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, uint32_t /* samples */, uint32_t /* current_frame */) override { + fuzzing::memory::memory_test(buffer, bytes); +#if 0 + try { + if ( ds.Get() == true ) { + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + } catch ( ... ) { } +#endif + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + }; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzzing::datasource::Datasource ds(data, size); + FLAC::Encoder::FuzzerStream encoder(ds); + + const int channels = 2; + encoder.set_channels(channels); + encoder.set_bits_per_sample(16); + + try { + ::FLAC__StreamEncoderInitStatus ret; + + if ( ds.Get() ) { + ret = encoder.init(); + } else { + ret = encoder.init_ogg(); + } + + if ( ret != FLAC__STREAM_ENCODER_INIT_STATUS_OK ) { + goto end; + } + + { + const bool res = encoder.set_streamable_subset(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_ogg_serial_number(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_verify(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_compression_level(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_do_exhaustive_model_search(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_do_mid_side_stereo(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_loose_mid_side_stereo(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const auto s = ds.Get(); + const bool res = encoder.set_apodization(s.data()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_max_lpc_order(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_qlp_coeff_precision(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_do_qlp_coeff_prec_search(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_do_escape_coding(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_min_residual_partition_order(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_max_residual_partition_order(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_rice_parameter_search_dist(ds.Get()); + fuzzing::memory::memory_test(res); + } + { + const bool res = encoder.set_total_samples_estimate(ds.Get()); + fuzzing::memory::memory_test(res); + } + + while ( ds.Get() ) { + { + auto dat = ds.GetVector(); + for (size_t i = 0; i < dat.size(); i++) { + if ( SAMPLE_VALUE_LIMIT != 0 ) { + if ( dat[i] < -SAMPLE_VALUE_LIMIT ) { + dat[i] = -SAMPLE_VALUE_LIMIT; + } else if ( dat[i] > SAMPLE_VALUE_LIMIT ) { + dat[i] = SAMPLE_VALUE_LIMIT; + } + } + } + const uint32_t samples = dat.size() / 2; + if ( samples > 0 ) { + const int32_t* ptr = dat.data(); + const bool res = encoder.process_interleaved(ptr, samples); + fuzzing::memory::memory_test(res); + } + } + } + } catch ( ... ) { } + +end: + { + const bool res = encoder.finish(); + fuzzing::memory::memory_test(res); + } + return 0; +} diff --git a/oss-fuzz/fuzz-encoder.dict b/oss-fuzz/fuzz-encoder.dict new file mode 100644 index 00000000..fe5b77fc --- /dev/null +++ b/oss-fuzz/fuzz-encoder.dict @@ -0,0 +1,17 @@ +"bartlett" +"bartlett_hann" +"blackman" +"blackman_harris_4term_92db" +"connes" +"flattop" +"gauss()" +"hamming" +"hann" +"kaiser_bessel" +"nuttall" +"rectangle" +"triangle" +"tukey(0)" +"partial_tukey(0)" +"punchout_tukey(0)" +"welch" -- cgit v1.2.1