From 05c1aa18e53258fefee9a8aac656280475ccf614 Mon Sep 17 00:00:00 2001 From: Ben Smith Date: Mon, 3 Aug 2020 11:31:46 -0700 Subject: [PATCH] Add tools to use LLVM's libFuzzer (#1507) This is useful for reproducing bugs found by oss-fuzz (see https://bugs.chromium.org/p/oss-fuzz/issues/list?q=wabt) --- CMakeLists.txt | 39 +++++++++++++++++++++++++++++++++----- Makefile | 14 +++++--------- README.md | 24 +++++++++++++++++++++-- src/tools/wasm2wat-fuzz.cc | 30 +++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 src/tools/wasm2wat-fuzz.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 5611ed7de..6b500eabc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ endif () option(BUILD_TESTS "Build GTest-based tests" ON) option(USE_SYSTEM_GTEST "Use system GTest, instead of building" OFF) option(BUILD_TOOLS "Build wabt commandline tools" ON) +option(BUILD_FUZZ_TOOLS "Build tools that can repro fuzz bugs" OFF) option(BUILD_LIBWASM "Build libwasm" ON) option(USE_ASAN "Use address sanitizer" OFF) option(USE_MSAN "Use memory sanitizer" OFF) @@ -344,6 +345,15 @@ set(WABT_LIBRARY_SRC add_library(wabt STATIC ${WABT_LIBRARY_SRC}) +if (BUILD_FUZZ_TOOLS) + set(FUZZ_FLAGS "-fsanitize=fuzzer,address") + add_library(wabt-fuzz STATIC ${WABT_LIBRARY_SRC}) + set_target_properties(wabt-fuzz + PROPERTIES + COMPILE_FLAGS "${FUZZ_FLAGS}" + ) +endif () + # libwasm, which implenents the wasm C API if (BUILD_LIBWASM) add_library(wasm SHARED ${WABT_LIBRARY_SRC} src/interp/interp-wasm-c-api.cc) @@ -369,10 +379,15 @@ endif () include(CMakeParseArguments) function(wabt_executable) - cmake_parse_arguments(EXE "WITH_LIBM;INSTALL" "NAME" "SOURCES;LIBS" ${ARGN}) + cmake_parse_arguments(EXE "WITH_LIBM;FUZZ;INSTALL" "NAME" "SOURCES;LIBS" ${ARGN}) # Always link libwabt. - set(EXE_LIBS "${EXE_LIBS};wabt") + if (EXE_FUZZ) + set(EXE_LIBS "${EXE_LIBS};wabt-fuzz") + set(EXTRA_LINK_FLAGS "${FUZZ_FLAGS}") + else () + set(EXE_LIBS "${EXE_LIBS};wabt") + endif () # Optionally link libm. if (EXE_WITH_LIBM AND (COMPILER_IS_CLANG OR COMPILER_IS_GNU)) @@ -388,12 +403,16 @@ function(wabt_executable) if (EMSCRIPTEN) # build to JS for now, as node.js doesn't have code caching for wasm yet, # and wasm startup times are slower - set_target_properties(${EXE_NAME} - PROPERTIES - LINK_FLAGS "-s NODERAWFS -s SINGLE_FILE -s WASM=0 -Oz -s ALLOW_MEMORY_GROWTH=1" + set(EXTRA_LINK_FLAGS + "${EXTRA_LINK_FLAGS} -s NODERAWFS -s SINGLE_FILE -s WASM=0 -Oz -s ALLOW_MEMORY_GROWTH=1" ) endif () + set_target_properties(${EXE_NAME} + PROPERTIES + LINK_FLAGS "${EXTRA_LINK_FLAGS}" + ) + if (EXE_INSTALL) list(APPEND WABT_EXECUTABLES ${EXE_NAME}) set(WABT_EXECUTABLES ${WABT_EXECUTABLES} PARENT_SCOPE) @@ -502,6 +521,16 @@ if (BUILD_TOOLS) SOURCES src/tools/wasm-decompile.cc INSTALL ) + + if(BUILD_FUZZ_TOOLS) + # wasm2wat-fuzz + wabt_executable( + NAME wasm2wat-fuzz + SOURCES src/tools/wasm2wat-fuzz.cc + FUZZ + INSTALL + ) + endif () endif () # Python 3.5 is the version shipped in Ubuntu Xenial diff --git a/Makefile b/Makefile index d7e5bb9f8..87f2c9a7e 100644 --- a/Makefile +++ b/Makefile @@ -20,23 +20,19 @@ MAKEFILE_NAME := $(lastword $(MAKEFILE_LIST)) ROOT_DIR := $(dir $(abspath $(MAKEFILE_NAME))) USE_NINJA ?= 0 -FUZZ_BIN_DIR ?= ${ROOT_DIR}/afl-fuzz -GCC_FUZZ_CC := ${FUZZ_BIN_DIR}/afl-gcc -GCC_FUZZ_CXX := ${FUZZ_BIN_DIR}/afl-g++ EMSCRIPTEN_DIR ?= $(dir $(shell which emcc)) CMAKE_CMD ?= cmake DEFAULT_SUFFIX = clang-debug -COMPILERS := GCC GCC_I686 GCC_FUZZ CLANG CLANG_I686 EMCC +COMPILERS := GCC GCC_I686 CLANG CLANG_I686 EMCC BUILD_TYPES := DEBUG RELEASE -SANITIZERS := ASAN MSAN LSAN UBSAN +SANITIZERS := ASAN MSAN LSAN UBSAN FUZZ CONFIGS := NORMAL $(SANITIZERS) COV NO_TESTS # directory names GCC_DIR := gcc/ GCC_I686_DIR := gcc-i686/ -GCC_FUZZ_DIR := gcc-fuzz/ CLANG_DIR := clang/ CLANG_I686_DIR := clang-i686/ EMCC_DIR := emscripten/ @@ -47,6 +43,7 @@ ASAN_DIR := asan/ MSAN_DIR := msan/ LSAN_DIR := lsan/ UBSAN_DIR := ubsan/ +FUZZ_DIR := fuzz/ COV_DIR := cov/ NO_TESTS_DIR := no-tests/ @@ -54,10 +51,8 @@ NO_TESTS_DIR := no-tests/ GCC_FLAG := -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ GCC_I686_FLAG := -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ \ -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -GCC_FUZZ_FLAG := -DCMAKE_C_COMPILER=${GCC_FUZZ_CC} -DCMAKE_CXX_COMPILER=${GCC_FUZZ_CXX} -DWITH_EXCEPTIONS=ON CLANG_FLAG := -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ CLANG_I686_FLAG := -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ - -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 EMCC_FLAG := -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_DIR}/cmake/Modules/Platform/Emscripten.cmake DEBUG_FLAG := -DCMAKE_BUILD_TYPE=Debug RELEASE_FLAG := -DCMAKE_BUILD_TYPE=Release @@ -66,13 +61,13 @@ ASAN_FLAG := -DUSE_ASAN=ON MSAN_FLAG := -DUSE_MSAN=ON LSAN_FLAG := -DUSE_LSAN=ON UBSAN_FLAG := -DUSE_UBSAN=ON +FUZZ_FLAG := -DBUILD_FUZZ_TOOLS=ON COV_FLAG := -DCODE_COVERAGE=ON NO_TESTS_FLAG := -DBUILD_TESTS=OFF # make target prefixes GCC_PREFIX := gcc GCC_I686_PREFIX := gcc-i686 -GCC_FUZZ_PREFIX := gcc-fuzz CLANG_PREFIX := clang CLANG_I686_PREFIX := clang-i686 EMCC_PREFIX := emscripten @@ -83,6 +78,7 @@ ASAN_PREFIX := -asan MSAN_PREFIX := -msan LSAN_PREFIX := -lsan UBSAN_PREFIX := -ubsan +FUZZ_PREFIX := -fuzz COV_PREFIX := -cov NO_TESTS_PREFIX := -no-tests diff --git a/README.md b/README.md index df101ef8d..3363b052c 100644 --- a/README.md +++ b/README.md @@ -120,9 +120,9 @@ There are many make targets available for other configurations as well. They are generated from every combination of a compiler, build type and configuration. - - compilers: `gcc`, `clang`, `gcc-i686`, `gcc-fuzz` + - compilers: `gcc`, `clang`, `gcc-i686`, `emcc` - build types: `debug`, `release` - - configurations: empty, `asan`, `msan`, `lsan`, `ubsan`, `no-tests` + - configurations: empty, `asan`, `msan`, `lsan`, `ubsan`, `fuzz`, `no-tests` They are combined with dashes, for example: @@ -333,3 +333,23 @@ $ CC=gcc scripts/travis-test.sh $ CC=clang scripts/travis-build.sh $ CC=clang scripts/travis-test.sh ``` + +## Fuzzing + +To build using the [LLVM fuzzer support](https://llvm.org/docs/LibFuzzer.html), +append `fuzz` to the target: + +```console +$ make clang-debug-fuzz +``` + +This will produce a `wasm2wat_fuzz` binary. It can be used to fuzz the binary +reader, as well as reproduce fuzzer errors found by +[oss-fuzz](https://github.com/google/oss-fuzz/tree/master/projects/wabt). + +```console +$ out/clang/Debug/fuzz/wasm2wat_fuzz ... +``` + +See the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html) for +more information about how to use this tool. diff --git a/src/tools/wasm2wat-fuzz.cc b/src/tools/wasm2wat-fuzz.cc new file mode 100644 index 000000000..1318ef628 --- /dev/null +++ b/src/tools/wasm2wat-fuzz.cc @@ -0,0 +1,30 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is copied from the oss-fuzz project: +// +// https://github.com/google/oss-fuzz/blob/master/projects/wabt/wasm2wat_fuzzer.cc + +#include "src/binary-reader-ir.h" +#include "src/binary-reader.h" +#include "src/common.h" +#include "src/ir.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + wabt::ReadBinaryOptions options; + wabt::Errors errors; + wabt::Module module; + wabt::ReadBinaryIr("dummy filename", data, size, options, &errors, &module); + return 0; +}