Add ossfuzz support for libsndfile

Fixes #476

Add a fuzzer (`sndfile_fuzzer`) which can be compiled and run
by the OSS-Fuzz infrastructure.
This commit is contained in:
Max Dymond 2019-07-27 20:44:18 +01:00 committed by evpobr
parent 0642efd52e
commit 20c65fecd9
9 changed files with 355 additions and 2 deletions

View File

@ -10,6 +10,7 @@ jobs:
name: [
ubuntu-gcc-autotools,
ubuntu-clang-autotools,
ubuntu-gcc-ossfuzz,
macos-autotools,
ubuntu-gcc-cmake,
ubuntu-gcc-cmake-shared,
@ -35,6 +36,12 @@ jobs:
cxx: clang++
build-system: autotools
- name: ubuntu-gcc-ossfuzz
os: ubuntu-latest
cc: gcc
cxx: g++
build-system: ossfuzz
- name: macos-autotools
os: macos-latest
cc: clang
@ -209,3 +216,11 @@ jobs:
cmake .. -G "${{matrix.cmake-generator}}" ${{matrix.cmake-options}}
cmake --build . --config Release
ctest
- name: Configure, build and test with OSSFuzz
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
if: startsWith(matrix.build-system,'ossfuzz')
run: |
./ossfuzz/ci_oss.sh

View File

@ -26,6 +26,8 @@ cmake_files = cmake/ClipMode.cmake cmake/FindFLAC.cmake \
pkgconfig_DATA = sndfile.pc
noinst_PROGRAMS =
#===============================================================================
test: check
@ -435,6 +437,39 @@ man/sndfile-deinterleave.1: man/sndfile-interleave.1
-rm -f $@
cd $(top_srcdir)/man && $(LN_S) sndfile-interleave.1 sndfile-deinterleave.1
############
# ossfuzz/ #
############
if USE_OSSFUZZ_FLAG
FUZZ_FLAG = $(LIB_FUZZING_ENGINE)
FUZZ_LDADD =
else
if USE_OSSFUZZ_STATIC
FUZZ_LDADD = $(LIB_FUZZING_ENGINE)
FUZZ_FLAG =
else
FUZZ_LDADD = libstandaloneengine.la
FUZZ_FLAG =
endif
endif
if USE_OSSFUZZERS
noinst_PROGRAMS += \
ossfuzz/sndfile_fuzzer
noinst_LTLIBRARIES += \
ossfuzz/libstandaloneengine.la
endif
ossfuzz_sndfile_fuzzer_SOURCES = ossfuzz/sndfile_fuzzer.cc
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_libstandaloneengine_la_SOURCES = ossfuzz/standaloneengine.cc ossfuzz/testinput.h
ossfuzz_libstandaloneengine_la_CXXFLAGS = $(AM_CXXFLAGS)
#############
# programs/ #
#############

View File

@ -163,6 +163,16 @@ AC_ARG_ENABLE([test-coverage],
[AS_HELP_STRING([--enable-test-coverage], [enable test coverage])])
AM_CONDITIONAL([ENABLE_TEST_COVERAGE], [test "x$enable_test_coverage" = "xyes"])
AC_ARG_ENABLE([ossfuzzers],
[AS_HELP_STRING([--enable-ossfuzzers],
[Whether to generate the fuzzers for OSS-Fuzz])],
[have_ossfuzzers=yes], [have_ossfuzzers=no])
AM_CONDITIONAL([USE_OSSFUZZERS], [test "x$have_ossfuzzers" = "xyes"])
AC_SUBST([LIB_FUZZING_ENGINE])
AM_CONDITIONAL([USE_OSSFUZZ_FLAG], [test "x$LIB_FUZZING_ENGINE" = "x-fsanitize=fuzzer"])
AM_CONDITIONAL([USE_OSSFUZZ_STATIC], [test -f "$LIB_FUZZING_ENGINE"])
dnl ====================================================================================
dnl Check types and their sizes.

1
ossfuzz/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
sndfile_fuzzer

41
ossfuzz/ci_oss.sh Executable file
View File

@ -0,0 +1,41 @@
#!/bin/bash
set -ex
PROJECT_NAME=libsndfile
# Clone the oss-fuzz repository
git clone https://github.com/google/oss-fuzz.git /tmp/ossfuzz
# TODO: Verify that the GITHUB variables below are correct for a PR
env | grep "GITHUB_"
if [[ ! -d /tmp/ossfuzz/projects/${PROJECT_NAME} ]]
then
echo "Could not find the ${PROJECT_NAME} project in ossfuzz"
# Exit with a success code while the libsndfile project is not expected to exist
# on oss-fuzz.
exit 0
fi
# Work out which repo to clone from, inside Docker
if [[ -n ${GITHUB_BASE_REF} ]]
then
# Pull-request branch
REPO=${GITHUB_REPOSITORY}
BRANCH=${GITHUB_BASE_REF}
else
# Push build.
REPO=${GITHUB_REPOSITORY}
BRANCH=${GITHUB_REF}
fi
# Modify the oss-fuzz Dockerfile so that we're checking out the current branch on travis.
sed -i "s@https://github.com/erikd/libsndfile.git@-b ${BRANCH} https://github.com/${REPO}.git@" /tmp/ossfuzz/projects/${PROJECT_NAME}/Dockerfile
# Try and build the fuzzers
pushd /tmp/ossfuzz
python infra/helper.py build_image --pull ${PROJECT_NAME}
python infra/helper.py build_fuzzers ${PROJECT_NAME}
popd

28
ossfuzz/ossfuzz.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash -eu
# This script is called by the oss-fuzz main project when compiling the fuzz
# targets. This script is regression tested by ci_oss.sh.
# Save off the current folder as the build root.
export BUILD_ROOT=$PWD
echo "CC: ${CC:-}"
echo "CXX: ${CXX:-}"
echo "LIB_FUZZING_ENGINE: ${LIB_FUZZING_ENGINE:-}"
echo "CFLAGS: ${CFLAGS:-}"
echo "CXXFLAGS: ${CXXFLAGS:-}"
echo "OUT: ${OUT:-}"
export MAKEFLAGS+="-j$(nproc)"
# Install dependencies
apt-get -y install autoconf autogen automake libasound2-dev \
libflac-dev libogg-dev libopus-dev libtool libvorbis-dev pkg-config python
# Compile the fuzzer.
./autogen.sh
./configure --enable-werror --enable-ossfuzzers
make V=1
# Copy the fuzzer to the output directory.
cp -v ossfuzz/sndfile_fuzzer $OUT/

134
ossfuzz/sndfile_fuzzer.cc Normal file
View File

@ -0,0 +1,134 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sndfile.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;
switch (whence)
{
case SEEK_SET:
vf->offset = offset;
break ;
case SEEK_CUR:
vf->offset = vf->offset + offset;
break ;
case SEEK_END:
vf->offset = vf->length + offset;
break;
default:
break;
}
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;
}
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;
// 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)
{
// 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.
}
EXIT_LABEL:
if (sndfile != NULL)
{
sf_close(sndfile);
}
free(read_buffer);
return 0;
}

View File

@ -0,0 +1,86 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "testinput.h"
/**
* Main procedure for standalone fuzzing engine.
*
* Reads filenames from the argument array. For each filename, read the file
* into memory and then call the fuzzing interface with the data.
*/
int main(int argc, char **argv)
{
int ii;
for(ii = 1; ii < argc; ii++)
{
FILE *infile;
printf("[%s] ", argv[ii]);
/* Try and open the file. */
infile = fopen(argv[ii], "rb");
if(infile)
{
uint8_t *buffer = NULL;
size_t buffer_len;
printf("Opened.. ");
/* Get the length of the file. */
fseek(infile, 0L, SEEK_END);
buffer_len = ftell(infile);
/* Reset the file indicator to the beginning of the file. */
fseek(infile, 0L, SEEK_SET);
/* Allocate a buffer for the file contents. */
buffer = (uint8_t *)calloc(buffer_len, sizeof(uint8_t));
if(buffer)
{
size_t result;
/* Read all the text from the file into the buffer. */
result = fread(buffer, sizeof(uint8_t), buffer_len, infile);
if (result == buffer_len)
{
printf("Read %zu bytes, fuzzing.. ", buffer_len);
/* Call the fuzzer with the data. */
LLVMFuzzerTestOneInput(buffer, buffer_len);
printf("complete !!");
}
else
{
fprintf(stderr,
"Failed to read %zu bytes (result %zu)\n",
buffer_len,
result);
}
/* Free the buffer as it's no longer needed. */
free(buffer);
buffer = NULL;
}
else
{
fprintf(stderr,
"[%s] Failed to allocate %zu bytes \n",
argv[ii],
buffer_len);
}
/* Close the file as it's no longer needed. */
fclose(infile);
infile = NULL;
}
else
{
/* Failed to open the file. Maybe wrong name or wrong permissions? */
fprintf(stderr, "[%s] Open failed. \n", argv[ii]);
}
printf("\n");
}
}

3
ossfuzz/testinput.h Normal file
View File

@ -0,0 +1,3 @@
#include <inttypes.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);