Lagom: Add two-stage build for Fuzzers to enable fuzzing generated code

This allows us to fuzz the generated unicode and timezone database
helpers, and to fuzz things like LibJS using Fuzzilli to get proper
coverage of our unicode handling code.

Update the Azure CI to use the new two-stage build as well, and cleanup
some unused CMake options there.
This commit is contained in:
Andrew Kaster 2022-02-19 16:09:40 -07:00 committed by Linus Groh
parent bfa4bc6f2d
commit 0c95d9962c
Notes: sideshowbarker 2024-07-17 18:28:52 +09:00
4 changed files with 129 additions and 40 deletions

View file

@ -43,42 +43,38 @@ jobs:
${{ if eq(parameters.fuzzer, 'NoFuzz') }}:
with_remote_data_caches: true
- script: |
mkdir -p Meta/Lagom/Build
displayName: 'Create Build Directory'
- ${{ if eq(parameters.fuzzer, 'Fuzz') }}:
- script: |
cmake -GNinja \
cmake -GNinja -B tools-build \
-DBUILD_LAGOM=OFF \
-DENABLE_LAGOM_CCACHE=ON \
-DCMAKE_INSTALL_PREFIX=tool-install
ninja -C tools-build install
cmake -GNinja -B Build \
-DBUILD_LAGOM=ON \
-DENABLE_LAGOM_CCACHE=ON \
-DENABLE_FUZZER_SANITIZER=ON \
-DENABLE_ADDRESS_SANITIZER=ON \
-DENABLE_PCI_IDS_DOWNLOAD=OFF \
-DENABLE_USB_IDS_DOWNLOAD=OFF \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
..
-DCMAKE_PREFIX_PATH=tool-install
displayName: 'Create Build Environment'
workingDirectory: $(Build.SourcesDirectory)/Meta/Lagom/Build
workingDirectory: $(Build.SourcesDirectory)/Meta/Lagom
env:
CCACHE_DIR: '$(SERENITY_CCACHE_DIR)'
- ${{ if eq(parameters.fuzzer, 'NoFuzz') }}:
- script: |
cmake -GNinja \
cmake -GNinja -B Build \
-DBUILD_LAGOM=ON \
-DENABLE_LAGOM_CCACHE=ON \
-DINCLUDE_WASM_SPEC_TESTS=ON \
-DWASM_SPEC_TEST_SKIP_FORMATTING=ON \
-DENABLE_UNDEFINED_SANITIZER=ON \
-DENABLE_ADDRESS_SANITIZER=ON \
-DENABLE_PCI_IDS_DOWNLOAD=OFF \
-DENABLE_USB_IDS_DOWNLOAD=OFF \
-DCMAKE_C_COMPILER=gcc-11 \
-DCMAKE_CXX_COMPILER=g++-11 \
..
-DCMAKE_CXX_COMPILER=g++-11
displayName: 'Create Build Environment'
workingDirectory: $(Build.SourcesDirectory)/Meta/Lagom/Build
workingDirectory: $(Build.SourcesDirectory)/Meta/Lagom
env:
PATH: '$(PATH):$(Build.SourcesDirectory)/wabt-1.0.23/bin'
CCACHE_DIR: '$(SERENITY_CCACHE_DIR)'

84
Meta/Lagom/BuildFuzzers.sh Executable file
View file

@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -e
BEST_CLANG_CANDIDATE=""
die() {
>&2 echo "die: $*"
exit 1
}
pick_clang() {
local BEST_VERSION=0
for CLANG_CANDIDATE in clang clang-13 clang-14 /usr/local/bin/clang-13 /usr/local/bin/clang-14; do
if ! command -v $CLANG_CANDIDATE >/dev/null 2>&1; then
continue
fi
if $CLANG_CANDIDATE --version 2>&1 | grep "Apple clang" >/dev/null; then
continue
fi
if ! $CLANG_CANDIDATE -dumpversion >/dev/null 2>&1; then
continue
fi
local VERSION=""
VERSION="$($CLANG_CANDIDATE -dumpversion)"
local MAJOR_VERSION="${VERSION%%.*}"
if [ "$MAJOR_VERSION" -gt "$BEST_VERSION" ]; then
BEST_VERSION=$MAJOR_VERSION
BEST_CLANG_CANDIDATE="$CLANG_CANDIDATE"
fi
done
if [ "$BEST_VERSION" -lt 13 ]; then
die "Please make sure that Clang version 13 or higher is installed."
fi
}
# Save flags for oss-fuzz to avoid fuzzing Tools/
# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#temporarily-disabling-code-instrumentation-during-builds
CFLAGS_SAVE="$CFLAGS"
CXXFLAGS_SAVE="$CXXFLAGS"
unset CFLAGS
unset CXXFLAGS
export AFL_NOOPT=1
# FIXME: Replace these CMake invocations with a CMake superbuild?
echo "Building Lagom Tools..."
cmake -GNinja -B Build/tools \
-DBUILD_LAGOM=OFF \
-DCMAKE_INSTALL_PREFIX=Build/tool-install
ninja -C Build/tools install
# Restore flags for oss-fuzz
export CFLAGS="${CFLAGS_SAVE}"
export CXXFLAGS="${CXXFLAGS_SAVE}"
unset AFL_NOOPT
echo "Building Lagom Fuzzers..."
if [ "$#" -gt "0" ] && [ "--oss-fuzz" = "$1" ] ; then
echo "Building for oss-fuzz configuration..."
cmake -GNinja -B Build/fuzzers \
-DBUILD_LAGOM=ON \
-DBUILD_SHARED_LIBS=OFF \
-DENABLE_OSS_FUZZ=ON \
-DCMAKE_C_COMPILER="$CC" \
-DCMAKE_CXX_COMPILER="$CXX" \
-DCMAKE_CXX_FLAGS="$CXXFLAGS -DOSS_FUZZ=ON" \
-DLINKER_FLAGS="$LIB_FUZZING_ENGINE" \
-DCMAKE_PREFIX_PATH=Build/tool-install
ninja -C Build/fuzzers
cp Build/fuzzers/Fuzzers/Fuzz* "$OUT"/
else
echo "Building for local fuzz configuration..."
pick_clang
cmake -GNinja -B Build/lagom-fuzzers \
-DBUILD_LAGOM=ON \
-DENABLE_FUZZER_SANITIZER=ON \
-DENABLE_ADDRESS_SANITIZER=ON \
-DENABLE_UNDEFINED_SANITIZER=ON \
-DCMAKE_PREFIX_PATH=Build/tool-install \
-DCMAKE_C_COMPILER=$BEST_CLANG_CANDIDATE \
-DCMAKE_CXX_COMPILER="${BEST_CLANG_CANDIDATE/clang/clang++}"
ninja -C Build/lagom-fuzzers
fi

View file

@ -41,14 +41,8 @@ endif()
# FIXME: BUILD_SHARED_LIBS has a default of OFF, as it's intended to be set by the
# user when configuring the project. We should instead change libjs-test262
# and oss-fuzz to set this option on their end, and enable it by default in
# Meta/serenity.sh
# This is #9867. We can change the oss-fuzz escape hatch to be a FATAL_ERROR
# message instead when implementing it.
# Meta/serenity.sh. This is #9867.
option(BUILD_SHARED_LIBS "Build shared libraries instead of static libraries" ON)
if (ENABLE_OSS_FUZZ)
# Don't use shared libraries on oss-fuzz, for ease of integration with their infrastructure
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries instead of static libraries" FORCE)
endif()
find_package(Threads REQUIRED)
@ -120,6 +114,12 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang$")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wno-expansion-to-defined)
if (ENABLE_FUZZER_SANITIZER)
message(FATAL_ERROR
"Fuzzer Sanitizer (-fsanitize=fuzzer) is only supported for Fuzzer targets with LLVM. "
"Reconfigure CMake with -DCMAKE_C_COMPILER and -DCMAKE_CXX_COMPILER pointing to a clang-based toolchain"
)
endif()
endif()
# These are here to support Fuzzili builds further down the directory stack
@ -168,8 +168,13 @@ function(lagom_lib library fs_name)
cmake_parse_arguments(LAGOM_LIBRARY "" "" "SOURCES;LIBS" ${ARGN})
set(target_name "Lagom${library}")
add_library(${target_name} ${LAGOM_LIBRARY_SOURCES})
# alias for parity with exports
add_library(Lagom::${library} ALIAS ${target_name})
# Don't make alias when we're going to import a previous build for Tools
# FIXME: Is there a better way to write this?
if (NOT ENABLE_OSS_FUZZ AND NOT ENABLE_FUZZER_SANITIZER)
# alias for parity with exports
add_library(Lagom::${library} ALIAS ${target_name})
endif()
set_target_properties(
${target_name} PROPERTIES
@ -247,11 +252,7 @@ endif()
# TimeZone
# This is needed even if Lagom is not enabled because it is depended upon by code generators.
if (NOT ENABLE_OSS_FUZZ AND NOT ENABLE_FUZZER_SANITIZER)
include(time_zone_data)
else()
set(ENABLE_TIME_ZONE_DATABASE_DOWNLOAD OFF)
endif()
include(time_zone_data)
file(GLOB LIBTIMEZONE_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTimeZone/*.cpp")
lagom_lib(TimeZone timezone
SOURCES ${LIBTIMEZONE_SOURCES} ${TIME_ZONE_DATA_SOURCES}
@ -268,7 +269,10 @@ install(
# Code Generators and other host tools
# We need to make sure not to build code generators for Fuzzer builds, as they already have their own main.cpp
if (NOT ENABLE_OSS_FUZZ AND NOT ENABLE_FUZZER_SANITIZER)
# Instead, we import them from a previous install of Lagom. This mandates a two-stage build for fuzzers.
if (ENABLE_OSS_FUZZ OR ENABLE_FUZZER_SANITIZER)
find_package(Lagom REQUIRED)
else()
add_subdirectory(Tools)
endif()
@ -442,12 +446,7 @@ if (BUILD_LAGOM)
)
# Unicode
# Don't include UnicodeData for Fuzzer builds, we didn't build the CodeGenerators
if (NOT ENABLE_OSS_FUZZ AND NOT ENABLE_FUZZER_SANITIZER)
include(unicode_data)
else()
set(ENABLE_UNICODE_DATABASE_DOWNLOAD OFF)
endif()
include(unicode_data)
file(GLOB LIBUNICODE_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibUnicode/*.cpp")
lagom_lib(Unicode unicode
SOURCES ${LIBUNICODE_SOURCES} ${UNICODE_DATA_SOURCES}

View file

@ -14,22 +14,32 @@ Lagom can be used to fuzz parts of SerenityOS's code base. Fuzzers can be run lo
### Fuzzing locally
Lagom can be used to fuzz parts of SerenityOS's code base. This requires building with `clang`, so it's convenient to use a different build directory for that. Fuzzers work best with Address Sanitizer enabled. Run CMake like this:
Lagom can be used to fuzz parts of SerenityOS's code base. This requires building with `clang`, so it's convenient to use a different build directory for that. Fuzzers work best with Address Sanitizer enabled. The fuzzer build requires code generators to be pre-built without fuzzing in a two stage build. To build with LLVM's libFuzzer, invoke
the ``BuildFuzzers.sh`` script with no arguments. The script does the equivalent of the CMake commands below:
# From the root of the SerenityOS checkout:
cmake -GNinja -S Meta/Lagom -B Build/lagom-fuzzers \
```sh
# From the Meta/Lagom directory:
# Stage 1: Build and install code generators and other tools
cmake -GNinja -B Build/tools \
-DBUILD_LAGOM=OFF \
-DCMAKE_INSTALL_PREFIX=Build/tool-install
ninja -C Build/tools install
# Stage 2: Build fuzzers, making sure the build can find the tools we just built
cmake -GNinja -B Build/lagom-fuzzers \
-DBUILD_LAGOM=ON \
-DENABLE_FUZZER_SANITIZER=ON \
-DENABLE_ADDRESS_SANITIZER=ON \
-DENABLE_UNDEFINED_SANITIZER=ON \
-DCMAKE_PREFIX_PATH=Build/tool-install \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_COMPILER=clang
cd Build/lagom-fuzzers
ninja
# Or as a handy rebuild-rerun line:
ninja FuzzJs && ./Fuzzers/FuzzJs
```
(Note that we require clang >= 12, so depending on your package manager you may need to specify `clang++-12` and `clang-12` instead.)
(Note that we require clang >= 13, see the pick_clang() function in the script for the paths that are searched)
Any fuzzing results (particularly slow inputs, crashes, etc.) will be dropped in the current directory.