From 110e26d9c2c96b65b85c74f7ac7230cadcc7656a Mon Sep 17 00:00:00 2001 From: DavidKorczynski Date: Sun, 15 Aug 2021 05:38:48 +0100 Subject: [PATCH] Add new fuzzer for OSS-Fuzz (#769) Adds a fuzzer targeting sf_read_double, sf_read_int and sf_read_short. Co-authored-by: evpobr --- CHANGELOG.md | 1 + Makefile.am | 8 +- ossfuzz/ossfuzz.sh | 1 + ossfuzz/sndfile_alt_fuzzer.cc | 79 +++++++++++++++++ ossfuzz/sndfile_fuzz_header.h | 119 +++++++++++++++++++++++++ ossfuzz/sndfile_fuzzer.cc | 158 +++++----------------------------- 6 files changed, 228 insertions(+), 138 deletions(-) create mode 100644 ossfuzz/sndfile_alt_fuzzer.cc create mode 100644 ossfuzz/sndfile_fuzz_header.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b21205a..c8d79a56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * ID3v1 reading * ID3v2 reading * Seeking +* New fuzzer for OSS-Fuzz, thanks @DavidKorczynski. * This `CHANGELOG.md`. All notable changes to this project will be documented in this file. The old `NEWS` file has been renamed to `NEWS.OLD` and is no longer updated. diff --git a/Makefile.am b/Makefile.am index b1f77571..77ab8aaf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -462,7 +462,8 @@ endif if USE_OSSFUZZERS noinst_PROGRAMS += \ - ossfuzz/sndfile_fuzzer + ossfuzz/sndfile_fuzzer \ + ossfuzz/sndfile_alt_fuzzer noinst_LTLIBRARIES += \ ossfuzz/libstandaloneengine.la @@ -473,6 +474,11 @@ ossfuzz_sndfile_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) ossfuzz_sndfile_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static ossfuzz_sndfile_fuzzer_LDADD = src/libsndfile.la $(FUZZ_LDADD) +ossfuzz_sndfile_alt_fuzzer_SOURCES = ossfuzz/sndfile_alt_fuzzer.cc +ossfuzz_sndfile_alt_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) +ossfuzz_sndfile_alt_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static +ossfuzz_sndfile_alt_fuzzer_LDADD = src/libsndfile.la $(FUZZ_LDADD) + ossfuzz_libstandaloneengine_la_SOURCES = ossfuzz/standaloneengine.cc ossfuzz/testinput.h ossfuzz_libstandaloneengine_la_CXXFLAGS = $(AM_CXXFLAGS) diff --git a/ossfuzz/ossfuzz.sh b/ossfuzz/ossfuzz.sh index b93d4fcc..a4d98d93 100755 --- a/ossfuzz/ossfuzz.sh +++ b/ossfuzz/ossfuzz.sh @@ -29,3 +29,4 @@ make V=1 # Copy the fuzzer to the output directory. cp -v ossfuzz/sndfile_fuzzer $OUT/ +cp -v ossfuzz/sndfile_alt_fuzzer $OUT/ diff --git a/ossfuzz/sndfile_alt_fuzzer.cc b/ossfuzz/sndfile_alt_fuzzer.cc new file mode 100644 index 00000000..e69fcdf3 --- /dev/null +++ b/ossfuzz/sndfile_alt_fuzzer.cc @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +#include "sndfile_fuzz_header.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ // One byte is needed for deciding which function to target. + if (size == 0) + return 0 ; + + const uint8_t decider = *data ; + data += 1 ; + size -= 1 ; + + SF_INFO sndfile_info ; + VIO_DATA vio_data ; + SF_VIRTUAL_IO vio ; + SNDFILE *sndfile = NULL ; + int err = sf_init_file(data, size, &sndfile, &vio_data, &vio, &sndfile_info) ; + if (err) + goto EXIT_LABEL ; + + // Just the right number of channels. Create some buffer space for reading. + switch (decider % 3) + { case 0 : + { + short* read_buffer = NULL ; + read_buffer = (short*)malloc(sizeof(short) * size); + if (read_buffer == NULL) + abort() ; + + while (sf_read_short(sndfile, read_buffer, size)) + { + // Do nothing with the data. + } + free(read_buffer) ; + } + break ; + case 1 : + { + int* read_buffer = NULL ; + read_buffer = (int*)malloc(sizeof(int) * size) ; + if (read_buffer == NULL) + abort() ; + + while (sf_read_int(sndfile, read_buffer, size)) + { + // Do nothing with the data. + } + free(read_buffer) ; + } + break ; + case 2 : + { + double* read_buffer = NULL ; + read_buffer = (double*)malloc(sizeof(double) * size) ; + if (read_buffer == NULL) + abort() ; + + while (sf_read_double(sndfile, read_buffer, size)) + { + // Do nothing with the data. + } + free(read_buffer) ; + } + break ; + default : + break ; + } ; + + EXIT_LABEL: + if (sndfile != NULL) + sf_close(sndfile); + + return 0 ; +} diff --git a/ossfuzz/sndfile_fuzz_header.h b/ossfuzz/sndfile_fuzz_header.h new file mode 100644 index 00000000..898aec44 --- /dev/null +++ b/ossfuzz/sndfile_fuzz_header.h @@ -0,0 +1,119 @@ +#ifndef SNDFILE_FUZZ_HEADER_H +#define SNDFILE_FUZZ_HEADER_H + +typedef struct +{ + sf_count_t offset ; + sf_count_t length ; + const unsigned char *data ; +} VIO_DATA ; + +static sf_count_t vfget_filelen (void *user_data) +{ VIO_DATA *vf = (VIO_DATA *)user_data ; + return vf->length ; +} + +static sf_count_t vfseek (sf_count_t offset, int whence, void *user_data) +{ + VIO_DATA *vf = (VIO_DATA *)user_data ; + sf_count_t new_offset ; + + switch (whence) + { case SEEK_SET : + new_offset = offset ; + break ; + + case SEEK_CUR : + new_offset = vf->offset + offset ; + break ; + + case SEEK_END : + new_offset = vf->length + offset ; + break ; + + default : + break ; + } + + /* Ensure you can't seek outside the data */ + if (new_offset > vf->length) + { /* Trying to seek past the end of the data */ + printf("vf overseek: new_offset(%" PRId64 ") > vf->length(%" PRId64 ");" + " whence(%d), vf->offset(%" PRId64 "), offset(%" PRId64 ")\n", + new_offset, vf->length, whence, vf->offset, offset) ; + new_offset = vf->length ; + } + else if (new_offset < 0) + { /* Trying to seek before the start of the data */ + printf("vf underseek: new_offset(%" PRId64 ") < 0; whence(%d), vf->offset" + "(%" PRId64 "), vf->length(%" PRId64 "), offset(%" PRId64 ")\n", + new_offset, whence, vf->offset, vf->length, offset) ; + new_offset = 0 ; + } + vf->offset = new_offset ; + + return vf->offset ; +} + +static sf_count_t vfread (void *ptr, sf_count_t count, void *user_data) +{ VIO_DATA *vf = (VIO_DATA *)user_data ; + + if (vf->offset + count > vf->length) + count = vf->length - vf->offset ; + + memcpy(ptr, vf->data + vf->offset, count) ; + vf->offset += count ; + + return count ; +} + +static sf_count_t vfwrite (const void *ptr, sf_count_t count, void *user_data) +{ + (void)ptr ; + (void)count ; + (void)user_data ; + + // Cannot write to this virtual file. + return 0; +} + +static sf_count_t vftell (void *user_data) +{ VIO_DATA *vf = (VIO_DATA *)user_data ; + + return vf->offset ; +} + +int sf_init_file(const uint8_t *data, + size_t size, + SNDFILE **sndfile, + VIO_DATA *vio_data, + SF_VIRTUAL_IO *vio, SF_INFO *sndfile_info) +{ float* read_buffer = NULL ; + + // Initialize the virtual IO structure. + vio->get_filelen = vfget_filelen ; + vio->seek = vfseek ; + vio->read = vfread ; + vio->write = vfwrite ; + vio->tell = vftell ; + + // Initialize the VIO user data. + vio_data->data = data ; + vio_data->length = size ; + vio_data->offset = 0 ; + + memset(sndfile_info, 0, sizeof(SF_INFO)) ; + + // Try and open the virtual file. + *sndfile = sf_open_virtual(vio, SFM_READ, sndfile_info, vio_data) ; + + if (sndfile_info->channels == 0) + return -1 ; + + if (sndfile_info->channels > 1024 * 1024) + return -1 ; + + return 0; +} + +#endif diff --git a/ossfuzz/sndfile_fuzzer.cc b/ossfuzz/sndfile_fuzzer.cc index 3c850730..2a3d6934 100644 --- a/ossfuzz/sndfile_fuzzer.cc +++ b/ossfuzz/sndfile_fuzzer.cc @@ -5,151 +5,35 @@ #include #include -typedef struct -{ - sf_count_t offset; - sf_count_t length; - const unsigned char *data; -} VIO_DATA; - -static sf_count_t vfget_filelen (void *user_data) -{ - VIO_DATA *vf = (VIO_DATA *)user_data; - return vf->length; -} - -static sf_count_t vfseek (sf_count_t offset, int whence, void *user_data) -{ - VIO_DATA *vf = (VIO_DATA *)user_data; - sf_count_t new_offset; - - switch (whence) - { - case SEEK_SET: - new_offset = offset; - break ; - - case SEEK_CUR: - new_offset = vf->offset + offset; - break ; - - case SEEK_END: - new_offset = vf->length + offset; - break; - - default: - break; - } - - /* Ensure you can't seek outside the data */ - if (new_offset > vf->length) - { - /* Trying to seek past the end of the data */ - printf("vf overseek: new_offset(%" PRId64 ") > vf->length(%" PRId64 ");" - " whence(%d), vf->offset(%" PRId64 "), offset(%" PRId64 ")\n", - new_offset, vf->length, whence, vf->offset, offset); - new_offset = vf->length; - } - else if (new_offset < 0) - { - /* Trying to seek before the start of the data */ - printf("vf underseek: new_offset(%" PRId64 ") < 0; whence(%d), vf->offset" - "(%" PRId64 "), vf->length(%" PRId64 "), offset(%" PRId64 ")\n", - new_offset, whence, vf->offset, vf->length, offset); - new_offset = 0; - } - vf->offset = new_offset; - - return vf->offset; -} - -static sf_count_t vfread (void *ptr, sf_count_t count, void *user_data) -{ - VIO_DATA *vf = (VIO_DATA *)user_data; - - if (vf->offset + count > vf->length) - { - count = vf->length - vf->offset; - } - - memcpy(ptr, vf->data + vf->offset, count); - vf->offset += count; - - return count; -} - -static sf_count_t vfwrite (const void *ptr, sf_count_t count, void *user_data) -{ - (void)ptr; - (void)count; - (void)user_data; - - // Cannot write to this virtual file. - return 0; -} - -static sf_count_t vftell (void *user_data) -{ VIO_DATA *vf = (VIO_DATA *)user_data; - - return vf->offset; -} +#include "sndfile_fuzz_header.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) -{ - VIO_DATA vio_data; - SF_VIRTUAL_IO vio; - SF_INFO sndfile_info; - SNDFILE *sndfile = NULL; - float* read_buffer = NULL; +{ VIO_DATA vio_data ; + SF_VIRTUAL_IO vio ; + SF_INFO sndfile_info ; + SNDFILE *sndfile = NULL ; + float* read_buffer = NULL ; - // Initialize the virtual IO structure. - vio.get_filelen = vfget_filelen; - vio.seek = vfseek; - vio.read = vfread; - vio.write = vfwrite; - vio.tell = vftell; + int err = sf_init_file(data, size, &sndfile, &vio_data, &vio, &sndfile_info) ; + if (err) + goto EXIT_LABEL ; - // Initialize the VIO user data. - vio_data.data = data; - vio_data.length = size; - vio_data.offset = 0; + // Just the right number of channels. Create some buffer space for reading. + read_buffer = (float*)malloc(sizeof(float) * sndfile_info.channels); + if (read_buffer == NULL) + abort() ; - memset(&sndfile_info, 0, sizeof(SF_INFO)); - - // Try and open the virtual file. - sndfile = sf_open_virtual(&vio, SFM_READ, &sndfile_info, &vio_data); - - if (sndfile_info.channels == 0) - { - // No sound channels in file. - goto EXIT_LABEL; - } - else if (sndfile_info.channels > 1024 * 1024) - { - // Too many channels to handle. - goto EXIT_LABEL; - } - - // Just the right number of channels. Create some buffer space for reading. - read_buffer = (float*)malloc(sizeof(float) * sndfile_info.channels); - if (read_buffer == NULL) - { - abort(); - } - - while (sf_readf_float(sndfile, read_buffer, 1)) - { - // Do nothing with the data. - } + while (sf_readf_float(sndfile, read_buffer, 1)) + { + // Do nothing with the data. + } EXIT_LABEL: - if (sndfile != NULL) - { - sf_close(sndfile); - } + if (sndfile != NULL) + sf_close(sndfile) ; - free(read_buffer); + free(read_buffer) ; - return 0; + return 0 ; }