Browse Source

Merge 968bdd099d into f2fc1ca00a

pull/583/merge
Chris Wolfe GitHub 3 years ago
parent
commit
088deea2cc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 30 deletions
  1. +1
    -0
      CMakeLists.txt
  2. +36
    -0
      fuzz/CMakeLists.txt
  3. +20
    -0
      fuzz/README.md
  4. +13
    -30
      fuzz/build.sh
  5. +25
    -0
      fuzz/standalone_runner.cc
  6. +1
    -0
      fuzz/tests/valid.json

+ 1
- 0
CMakeLists.txt View File

@@ -46,6 +46,7 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING AND
(NOT MSVC OR NOT (MSVC_VERSION LESS 1800)) # Tests need at least VS2013
)
add_subdirectory(tests)
add_subdirectory(fuzz)
endif()

if (NOT MSVC) # cmd line apps don't built on Windows currently.


+ 36
- 0
fuzz/CMakeLists.txt View File

@@ -0,0 +1,36 @@
# https://cmake.org/cmake/help/v3.0/command/add_test.html
# https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
enable_language(CXX)
set (CMAKE_CXX_STANDARD 11)

include_directories(PUBLIC ${CMAKE_SOURCE_DIR})
include_directories(PUBLIC ${PROJECT_BINARY_DIR})

# This sets up either the standalone runner or the user supplied libfuzzing
# engine. We're using the standalone runner for unit testing alone at the
# moment to make the sure the fuzzer builds and runs appropriately.
if(DEFINED ENV{LIB_FUZZING_ENGINE})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{LIB_FUZZING_ENGINE}")
set(FUZZING_ENGINE "")
else()
add_library(fuzzing_engine standalone_runner.cc)
set(FUZZING_ENGINE fuzzing_engine)
endif()

foreach(FUZZERNAME
tokener_parse_ex_fuzzer)

add_executable(${FUZZERNAME} ${FUZZERNAME}.cc)
target_link_libraries(${FUZZERNAME}
json-c
${FUZZING_ENGINE})

file(GLOB TESTFILES "${CMAKE_SOURCE_DIR}/fuzz/tests/*.json")
foreach(TESTFILE ${TESTFILES})
get_filename_component(FILENAME ${TESTFILE} NAME_WE)
add_test(NAME test_${FUZZERNAME}_${FILENAME}
COMMAND ${CMAKE_BINARY_DIR}/fuzz/${FUZZERNAME}
${TESTFILE})

endforeach(TESTFILE)
endforeach(FUZZERNAME)

+ 20
- 0
fuzz/README.md View File

@@ -4,3 +4,23 @@ This directory contains fuzzers that
target [llvm's LibFuzzer](https://llvm.org/docs/LibFuzzer.html). They are built
and run automatically by
Google's [OSS-Fuzz](https://github.com/google/oss-fuzz/) infrastructure.

## How do I test or run the fuzzers like oss-fuzz?

```
git clone https://github.com/google/oss-fuzz.git
cd oss-fuzz
python infra/helper.py build_image json-c
python infra/helper.py build_fuzzers --sanitizer address --engine libfuzzer --architecture x86_64 json-c
python infra/helper.py run_fuzzer json-c tokener_parse_ex_fuzzer
```

## How do I add new unit or regression tests for the fuzzer?

The tests directory contains json files that can be used to either test the fuzzer itself or be used as regression tests. As long as the files end in `.json`, cmake will pick them up and generate a Ctest test case. If/when oss-fuzz finds a bug with a fuzzer, simply pull that test case into the `./tests` directory and it will serve as a regression test.

Note - the fuzzers are not being run with sanitizers in this repository's CI at the moment; we're strictly building them here to ensure that they function.

## How do I reproduce a failure form a fuzzer?

Use [the steps detailed on OSS-fuzz](https://google.github.io/oss-fuzz/advanced-topics/reproducing/).

+ 13
- 30
fuzz/build.sh View File

@@ -14,39 +14,22 @@
# limitations under the License.
#
################################################################################
# this is expected to be run only by oss-fuzz. It will run from $SRC (above json-c)
cp $SRC/json-c/fuzz/*.dict "$OUT/"

# This should be run from the top of the json-c source tree.
BUILD="$SRC/json-c/build"

mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF ..
make -j$(nproc)

LIB=$(pwd)/libjson-c.a
cd ..
zip -j "$SRC/corpus.zip" "$SRC/go-fuzz-corpus/json/corpus"

# These seem to be set externally, but let's assign defaults to
# make it possible to at least partially test this standalone.
: ${SRC:=$(dirname "$0")}
: ${OUT:=$SRC/out}
: ${CXX:=gcc}
: ${CXXFLAGS:=}

[ -d "$OUT" ] || mkdir "$OUT"
cp $SRC/*.dict $OUT/.
mkdir "$BUILD"
cd "$BUILD"
cmake -DCMAKE_C_FLAGS="$CFLAGS" -DCMAKE_CXX_FLAGS="$CXXFLAGS" -DBUILD_SHARED_LIBS=OFF ..
make -j$(nproc)
cp fuzz/*_fuzzer "$OUT/"

# XXX this doesn't seem to make much sense, since $SRC is presumably
# the "fuzz" directory, which is _inside_ the json-c repo, rather than
# the other way around, but I'm just preserving existing behavior. -erh
INCS=$SRC/json-c
# Compat when testing standalone
[ -e "${INCS}" ] || ln -s .. "${INCS}"
fuzzerFiles=$(find fuzz/ -name "*_fuzzer")

set -x
set -v
for f in $SRC/*_fuzzer.cc; do
fuzzer=$(basename "$f" _fuzzer.cc)
$CXX $CXXFLAGS -std=c++11 -I$INCS \
$SRC/${fuzzer}_fuzzer.cc -o $OUT/${fuzzer}_fuzzer \
-lFuzzingEngine $LIB
for F in $fuzzerFiles; do
FN=$(basename -- $F)
cp "$SRC/corpus.zip" "$OUT/${FN}_seed_corpus.zip"
done

+ 25
- 0
fuzz/standalone_runner.cc View File

@@ -0,0 +1,25 @@
#include <cassert>
#include <fstream>
#include <iostream>
#include <vector>

// Forward declare the "fuzz target" interface.
// We deliberately keep this inteface simple and header-free.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

int main(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
std::ifstream in(argv[i]);
in.seekg(0, in.end);
size_t length = in.tellg();
in.seekg(0, in.beg);
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
// Allocate exactly length bytes so that we reliably catch buffer overflows.
std::vector<char> bytes(length);
in.read(bytes.data(), bytes.size());
assert(in);
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
bytes.size());
std::cout << "Execution successful" << std::endl;
}
}

+ 1
- 0
fuzz/tests/valid.json View File

@@ -0,0 +1 @@
{"foo":123}

Loading…
Cancel
Save