From c1dea4ee507ef62b121051e34e36a9b24459ea39 Mon Sep 17 00:00:00 2001 From: Phil Turnbull Date: Mon, 26 Jun 2017 15:05:30 -0400 Subject: Add Makefile target and harness to fuzz with libFuzzer This can be run locally with `make libFuzzer` but the harness will be integrated into oss-fuzz for large-scale fuzzing. --- CMakeLists.txt | 1 + Makefile | 8 ++++++++ README.md | 8 ++++++++ src/CMakeLists.txt | 11 +++++++++++ test/cmark-fuzz.c | 28 ++++++++++++++++++++++++++++ test/run-cmark-fuzz | 4 ++++ 6 files changed, 60 insertions(+) create mode 100644 test/cmark-fuzz.c create mode 100755 test/run-cmark-fuzz diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e60fd5..33180e5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_ option(CMARK_TESTS "Build cmark tests and enable testing" ON) option(CMARK_STATIC "Build static libcmark library" ON) option(CMARK_SHARED "Build shared libcmark library" ON) +option(CMARK_LIB_FUZZER "Build libFuzzer fuzzing harness" OFF) add_subdirectory(src) if(CMARK_TESTS AND CMARK_SHARED) diff --git a/Makefile b/Makefile index 1b58bf7..c10c035 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ BENCHFILE=$(BENCHDIR)/benchinput.md ALLTESTS=alltests.md NUMRUNS?=10 CMARK=$(BUILDDIR)/src/cmark +CMARK_FUZZ=$(BUILDDIR)/src/cmark-fuzz PROG?=$(CMARK) VERSION?=$(SPECVERSION) RELEASE?=CommonMark-$(VERSION) @@ -81,6 +82,13 @@ afl: -t 100 \ $(CMARK) $(CMARK_OPTS) +libFuzzer: + @[ -n "$(LIB_FUZZER_PATH)" ] || { echo '$$LIB_FUZZER_PATH not set'; false; } + mkdir -p $(BUILDDIR) + cd $(BUILDDIR) && cmake -DCMAKE_BUILD_TYPE=Asan -DCMARK_LIB_FUZZER=ON -DCMAKE_LIB_FUZZER_PATH=$(LIB_FUZZER_PATH) .. + $(MAKE) -j2 -C $(BUILDDIR) cmark-fuzz + test/run-cmark-fuzz $(CMARK_FUZZ) + clang-check: all ${CLANG_CHECK} -p build -analyze src/*.c diff --git a/README.md b/README.md index 9aaf32f..1c9dd69 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,13 @@ To do a more systematic fuzz test with [american fuzzy lop]: AFL_PATH=/path/to/afl_directory make afl +Fuzzing with [libFuzzer] is also supported but, because libFuzzer is still +under active development, may not work with your system-installed version of +clang. Assuming LLVM has been built in `$HOME/src/llvm/build` the fuzzer can be +run with: + + CC="$HOME/src/llvm/build/bin/clang" LIB_FUZZER_PATH="$HOME/src/llvm/lib/Fuzzer/libFuzzer.a" make libFuzzer + To make a release tarball and zip archive: make archive @@ -188,3 +195,4 @@ most of the C library's API and its test harness. [Build Status]: https://img.shields.io/travis/jgm/cmark/master.svg?style=flat [Windows Build Status]: https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true [american fuzzy lop]: http://lcamtuf.coredump.cx/afl/ +[libFuzzer]: http://llvm.org/docs/LibFuzzer.html diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f52ded6..3197196 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -186,3 +186,14 @@ endif() if(CMAKE_BUILD_TYPE STREQUAL "Ubsan") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined") endif() + +if(CMARK_LIB_FUZZER) + set(FUZZ_HARNESS "cmark-fuzz") + add_executable(${FUZZ_HARNESS} ../test/cmark-fuzz.c ${LIBRARY_SOURCES}) + target_link_libraries(${FUZZ_HARNESS} "${CMAKE_LIB_FUZZER_PATH}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-coverage=trace-pc-guard") + + # cmark is written in C but the libFuzzer runtime is written in C++ which + # needs to link against the C++ runtime. Explicitly link it into cmark-fuzz + set_target_properties(${FUZZ_HARNESS} PROPERTIES LINK_FLAGS "-lstdc++") +endif() diff --git a/test/cmark-fuzz.c b/test/cmark-fuzz.c new file mode 100644 index 0000000..f09db52 --- /dev/null +++ b/test/cmark-fuzz.c @@ -0,0 +1,28 @@ +#include +#include +#include "cmark.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + int options = 0; + if (size > sizeof(options)) { + /* First 4 bytes of input are treated as options */ + int options = *(const int *)data; + + /* Mask off valid option bits */ + options = options & (CMARK_OPT_SOURCEPOS | CMARK_OPT_HARDBREAKS | CMARK_OPT_SAFE | CMARK_OPT_NOBREAKS | CMARK_OPT_NORMALIZE | CMARK_OPT_VALIDATE_UTF8 | CMARK_OPT_SMART); + + /* Remainder of input is the markdown */ + const char *markdown = (const char *)(data + sizeof(options)); + const size_t markdown_size = size - sizeof(options); + cmark_node *doc = cmark_parse_document(markdown, markdown_size, options); + + free(cmark_render_commonmark(doc, options, 80)); + free(cmark_render_html(doc, options)); + free(cmark_render_latex(doc, options, 80)); + free(cmark_render_man(doc, options, 80)); + free(cmark_render_xml(doc, options)); + + cmark_node_free(doc); + } + return 0; +} diff --git a/test/run-cmark-fuzz b/test/run-cmark-fuzz new file mode 100755 index 0000000..75100b8 --- /dev/null +++ b/test/run-cmark-fuzz @@ -0,0 +1,4 @@ +#!/bin/bash -eu +CMARK_FUZZ="$1" +shift +ASAN_OPTIONS="quarantine_size_mb=10:detect_leaks=1" "${CMARK_FUZZ}" -max_len=256 -timeout=1 -dict=test/fuzzing_dictionary "$@" -- cgit v1.2.3