diff --git a/Meta/Azure/Lagom.yml b/Meta/Azure/Lagom.yml index 5366468612..5fbe64de1e 100644 --- a/Meta/Azure/Lagom.yml +++ b/Meta/Azure/Lagom.yml @@ -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)' diff --git a/Meta/Lagom/BuildFuzzers.sh b/Meta/Lagom/BuildFuzzers.sh new file mode 100755 index 0000000000..78371d8fdf --- /dev/null +++ b/Meta/Lagom/BuildFuzzers.sh @@ -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 diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 2b42dfa1fb..32b4eed19b 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -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} diff --git a/Meta/Lagom/ReadMe.md b/Meta/Lagom/ReadMe.md index aee06fef36..dcc4c6d9a3 100644 --- a/Meta/Lagom/ReadMe.md +++ b/Meta/Lagom/ReadMe.md @@ -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.