diff --git a/.travis.yml b/.travis.yml index 5b23c94..8dae9a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ env: - JANSSON_BUILD_METHOD=cmake JANSSON_CMAKE_OPTIONS="-DJANSSON_TEST_WITH_VALGRIND=ON" JANSSON_EXTRA_INSTALL="valgrind" - JANSSON_BUILD_METHOD=autotools - JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl" + - JANSSON_BUILD_METHOD=fuzzer language: c compiler: - gcc @@ -20,3 +21,4 @@ script: - if [ "$JANSSON_BUILD_METHOD" = "autotools" ]; then autoreconf -f -i && CFLAGS=-Werror ./configure && make check; fi - if [ "$JANSSON_BUILD_METHOD" = "cmake" ]; then mkdir build && cd build && cmake $JANSSON_CMAKE_OPTIONS .. && cmake --build . && ctest --output-on-failure; fi - if [ "$JANSSON_BUILD_METHOD" = "coverage" ]; then mkdir build && cd build && cmake $JANSSON_CMAKE_OPTIONS .. && cmake --build . && cmake --build . --target coveralls; fi + - if [ "$JANSSON_BUILD_METHOD" = "fuzzer" ]; then ./test/ossfuzz/travisoss.sh; fi diff --git a/configure.ac b/configure.ac index cf9ac33..1e6500b 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,7 @@ AC_CONFIG_HEADERS([jansson_private_config.h]) # Checks for programs. AC_PROG_CC +AC_PROG_CXX AC_PROG_LIBTOOL AM_CONDITIONAL([GCC], [test x$GCC = xyes]) @@ -136,6 +137,19 @@ fi AS_IF([test "x$with_Bsymbolic" = "xyes"], [JSON_BSYMBOLIC_LDFLAGS=-Wl[,]-Bsymbolic-functions]) AC_SUBST(JSON_BSYMBOLIC_LDFLAGS) + +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 "x$LIB_FUZZING_ENGINE"]) + + if test x$GCC = xyes; then AC_MSG_CHECKING(for -Wno-format-truncation) wnoformat_truncation="-Wno-format-truncation" @@ -156,6 +170,7 @@ AC_CONFIG_FILES([ src/jansson_config.h test/Makefile test/bin/Makefile + test/ossfuzz/Makefile test/suites/Makefile test/suites/api/Makefile ]) diff --git a/test/Makefile.am b/test/Makefile.am index 86d1614..344d18d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = bin suites +SUBDIRS = bin suites ossfuzz EXTRA_DIST = scripts run-suites TESTS = run-suites diff --git a/test/ossfuzz/.gitignore b/test/ossfuzz/.gitignore new file mode 100644 index 0000000..7fbb867 --- /dev/null +++ b/test/ossfuzz/.gitignore @@ -0,0 +1 @@ +json_load_dump_fuzzer diff --git a/test/ossfuzz/Makefile.am b/test/ossfuzz/Makefile.am new file mode 100644 index 0000000..a2e802e --- /dev/null +++ b/test/ossfuzz/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src +LDADD = $(top_builddir)/src/libjansson.la + +if USE_OSSFUZZ_FLAG +FUZZ_FLAG = $(LIB_FUZZING_ENGINE) +else +if USE_OSSFUZZ_STATIC +LDADD += $(LIB_FUZZING_ENGINE) +FUZZ_FLAG = +else +LDADD += libstandaloneengine.a +FUZZ_FLAG = +endif +endif + +noinst_PROGRAMS = +noinst_LIBRARIES = + +if USE_OSSFUZZERS +noinst_PROGRAMS += \ + json_load_dump_fuzzer + +noinst_LIBRARIES += \ + libstandaloneengine.a +endif + +json_load_dump_fuzzer_SOURCES = json_load_dump_fuzzer.cc testinput.h +json_load_dump_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) +json_load_dump_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static + +libstandaloneengine_a_SOURCES = standaloneengine.cc +libstandaloneengine_a_CXXFLAGS = $(AM_CXXFLAGS) diff --git a/test/ossfuzz/json_load_dump_fuzzer.cc b/test/ossfuzz/json_load_dump_fuzzer.cc new file mode 100644 index 0000000..09f52d2 --- /dev/null +++ b/test/ossfuzz/json_load_dump_fuzzer.cc @@ -0,0 +1,47 @@ +#include +#include + +#include "jansson.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + json_error_t error; + + if (size < sizeof(size_t) + sizeof(size_t)) + { + return 0; + } + + // Use the first sizeof(size_t) bytes as load flags. + size_t load_flags = *(const size_t*)data; + data += sizeof(size_t); + size -= sizeof(size_t); + + // Use the next sizeof(size_t) bytes as dump flags. + size_t dump_flags = *(const size_t*)data; + data += sizeof(size_t); + size -= sizeof(size_t); + + // Attempt to load the remainder of the data with the given load flags. + const char* text = reinterpret_cast(data); + json_t* jobj = json_loadb(text, size, load_flags, &error); + + if (jobj == NULL) + { + return 0; + } + + // Attempt to dump the loaded json object with the given dump flags. + char* out = json_dumps(jobj, dump_flags); + if (out) + { + free(out); + } + + if (jobj) + { + json_decref(jobj); + } + + return 0; +} \ No newline at end of file diff --git a/test/ossfuzz/ossfuzz.sh b/test/ossfuzz/ossfuzz.sh new file mode 100755 index 0000000..9d72e79 --- /dev/null +++ b/test/ossfuzz/ossfuzz.sh @@ -0,0 +1,27 @@ +#!/bin/bash -eu + +# This script is called by the oss-fuzz main project when compiling the fuzz +# targets. This script is regression tested by travisoss.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 automake libtool + +# Compile the fuzzer. +autoreconf -i +./configure --enable-ossfuzzers +make + +# Copy the fuzzer to the output directory. +cp -v test/ossfuzz/json_load_dump_fuzzer $OUT/ diff --git a/test/ossfuzz/standaloneengine.cc b/test/ossfuzz/standaloneengine.cc new file mode 100644 index 0000000..175360e --- /dev/null +++ b/test/ossfuzz/standaloneengine.cc @@ -0,0 +1,74 @@ +#include +#include +#include + +#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) + { + /* Read all the text from the file into the buffer. */ + fread(buffer, sizeof(uint8_t), buffer_len, infile); + printf("Read %zu bytes, fuzzing.. ", buffer_len); + + /* Call the fuzzer with the data. */ + LLVMFuzzerTestOneInput(buffer, buffer_len); + + printf("complete !!"); + + /* 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"); + } +} diff --git a/test/ossfuzz/testinput.h b/test/ossfuzz/testinput.h new file mode 100644 index 0000000..6ab9b51 --- /dev/null +++ b/test/ossfuzz/testinput.h @@ -0,0 +1,3 @@ +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); diff --git a/test/ossfuzz/travisoss.sh b/test/ossfuzz/travisoss.sh new file mode 100755 index 0000000..e99cc6e --- /dev/null +++ b/test/ossfuzz/travisoss.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -ex + +PROJECT_NAME=jansson + +# Clone the oss-fuzz repository +git clone https://github.com/google/oss-fuzz.git /tmp/ossfuzz + +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 jansson project is not expected to exist + # on oss-fuzz. + exit 0 +fi + +# Modify the oss-fuzz Dockerfile so that we're checking out the current branch on travis. +sed -i "s@https://github.com/akheron/jansson.git@-b $TRAVIS_BRANCH https://github.com/akheron/jansson.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