diff --git a/.github/workflows/sanitize.yaml b/.github/workflows/sanitize.yaml new file mode 100644 index 0000000000..9aa2642164 --- /dev/null +++ b/.github/workflows/sanitize.yaml @@ -0,0 +1,80 @@ +name: Sanitize with AddressSanitizer, MemorySanitizer, ThreadSanitizer and UndefinedBehaviorSanitizer. + +on: + pull_request_review: + types: [submitted] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build_dependencies: + name: Build deps [ubuntu] + runs-on: ubuntu-latest + if: github.event.review.state == 'APPROVED' + steps: + - uses: actions/checkout@v4 + - name: Get dependencies hash + id: get-hash + run: echo "deps_hash=`cat docker/build_deps.sh | shasum`" >> $GITHUB_OUTPUT + - name: Cache lookup + uses: actions/cache/restore@v4 + id: cache-lookup + with: + path: deps + key: deps-${{ steps.get-hash.outputs.deps_hash }} + lookup-only: true + - name: Set up dependencies + if: steps.cache-lookup.outputs.cache-hit != 'true' + run: | + sudo apt-get update + sudo apt-get install -qy build-essential \ + gdb \ + curl \ + python3.10 \ + python3-pip \ + cmake \ + ninja-build \ + pkg-config \ + bison \ + libfl-dev \ + libbenchmark-dev \ + libgmock-dev \ + libz-dev + - name: Fetch & Build non packaged dependencies + if: steps.cache-lookup.outputs.cache-hit != 'true' + run: | + mkdir -p deps + cd deps + ../docker/build_deps.sh + - name: Cache save + if: steps.cache-lookup.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: deps + key: deps-${{ steps.get-hash.outputs.deps_hash }} + + run_asan: + needs: build_dependencies + uses: ./.github/workflows/sanitizer-check.yaml + with: + sanitizer-name: 'asan' + + run_msan: + needs: build_dependencies + uses: ./.github/workflows/sanitizer-check.yaml + with: + sanitizer-name: 'msan' + + run_tsan: + needs: build_dependencies + uses: ./.github/workflows/sanitizer-check.yaml + with: + sanitizer-name: 'tsan' + + run_ubsan: + needs: build_dependencies + uses: ./.github/workflows/sanitizer-check.yaml + with: + sanitizer-name: 'ubsan' diff --git a/.github/workflows/sanitizer-check.yaml b/.github/workflows/sanitizer-check.yaml new file mode 100644 index 0000000000..4c4d69faef --- /dev/null +++ b/.github/workflows/sanitizer-check.yaml @@ -0,0 +1,27 @@ +name: Build and run Sanitizer + +on: + workflow_call: + inputs: + sanitizer-name: + description: Sanitizer name (asan/msan/tsan/ubsan) + type: string + required: true + +jobs: + build_and_run_sanitizer: + name: Build and run Sanitizer + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Get dependencies hash + id: get-hash + run: echo "deps_hash=`cat docker/build_deps.sh | shasum`" >> $GITHUB_OUTPUT + - uses: actions/cache/restore@v4 + with: + path: deps + key: deps-${{ steps.get-hash.outputs.deps_hash }} + - name: Build BlazingMQ and dependencies with sanitizer instrumentation + run: ${{ github.workspace }}/.github/workflows/sanitizers/build_sanitizer.sh ${{ inputs.sanitizer-name }} + - name: Run unit tests under sanitizer + run: ${{ github.workspace }}/cmake.bld/Linux/run-unittests.sh diff --git a/.github/workflows/sanitizers/build_sanitizer.sh b/.github/workflows/sanitizers/build_sanitizer.sh new file mode 100755 index 0000000000..435c38dac4 --- /dev/null +++ b/.github/workflows/sanitizers/build_sanitizer.sh @@ -0,0 +1,287 @@ +#!/usr/bin/env bash + +# This script can be used to build BlazingMQ, and all of its transitive +# dependencies (up to and including the standard library) using: +# - Clang +# - LLVM libc++ standard library +# - A CMake toolchain file specific for instrumented build +# It is currently used to build instrumented BlazingMQ binaries for CI for all +# Clang sanitizers (i.e. Address/Leak, Memory, Thread, UndefinedBehavior). +# +# It performs the following: +# 1) Install clang compiler. +# 2) Download llvm-project required for libc++ instrumentation. +# 3) Download external dependencies required for instrumentation. +# 4) Build libc++ with the instrumentation specified by . +# 5) Build sanitizer-instrumented dependencies including BDE, NTF, GoogleTest, +# Google Benchmark and zlib. +# 6) Build sanitizer-instrumented BlazingMQ unit tests. +# 7) Generate scripts to run unit tests: +# ./cmake.bld/Linux/run-unittests.sh +# This script is used as-is by CI to run unit tests under sanitizer. + +set -eux + +# :: Required arguments ::::::::::::::::::::::::::::::::::::::::::::::::::::::: +if [ -z "${1}" ]; then + echo 'Error: Missing sanitizer name.' >&2 + echo ' (Usage: build_sanitizer.sh )' >&2 + exit 1 +fi + +SANITIZER_NAME="${1}" + +# Github's 'ubuntu-22.04' image contains a lot of preinstalled tools, +# see https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md. +# Uninstall uneeded tools which cause of versions clash. +sudo apt-get purge \ + llvm-14 \ + clang-14 \ + gcc-9 \ + gcc-10 \ + gcc-11 \ + gcc-12 + +# Install prerequisites +sudo apt-get update && sudo apt-get install -qy \ + lsb-release \ + wget \ + software-properties-common \ + gnupg \ + git \ + curl \ + jq \ + ninja-build \ + bison \ + libfl-dev \ + pkg-config + +# Install prerequisites for LLVM: latest cmake version, Ubuntu apt repository contains cmake version 3.22.1 +wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null \ + | gpg --dearmor - \ + | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null +sudo apt-add-repository -y "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" +sudo apt-get install -qy cmake + +# Install LLVM +wget https://apt.llvm.org/llvm.sh +chmod +x llvm.sh +LLVM_VERSION=17 +sudo ./llvm.sh ${LLVM_VERSION} all + +# Create version-agnostic pointers to required LLVM binaries. +sudo ln -sf /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang +sudo ln -sf /usr/bin/clang++-${LLVM_VERSION} /usr/bin/clang++ +sudo ln -sf /usr/bin/llvm-symbolizer-${LLVM_VERSION} /usr/bin/llvm-symbolizer + +# Set some initial constants +PARALLELISM=8 + +DIR_ROOT="${PWD}" +DIR_SCRIPTS="${DIR_ROOT}/.github/workflows/sanitizers" +DIR_EXTERNAL="${DIR_ROOT}/deps" +DIR_SRCS_EXT="${DIR_EXTERNAL}/srcs" +DIR_BUILD_EXT="${DIR_EXTERNAL}/cmake.bld" + +DIR_SRC_BMQ="${DIR_ROOT}" +DIR_BUILD_BMQ="${DIR_SRC_BMQ}/cmake.bld/Linux" + +# Parse sanitizers config +cfgquery() { + jq "${1}" "${DIR_SCRIPTS}/sanitizers.json" --raw-output +} +LLVM_SANITIZER_NAME="$(cfgquery ."${SANITIZER_NAME}".llvm_sanitizer_name)" +# Check if llvm specific cmake options are present for the given sanitizer +LLVM_SPECIFIC_CMAKE_OPTIONS="$(cfgquery ."${SANITIZER_NAME}".llvm_specific_cmake_options)" +if [[ "$LLVM_SPECIFIC_CMAKE_OPTIONS" == null ]]; then LLVM_SPECIFIC_CMAKE_OPTIONS=""; fi + +checkoutGitRepo() { + local repo=$1 + local ref=$2 + local repoDir=$3 + echo "Checking out ${repo} at ${ref}" + + local repoPath="${DIR_SRCS_EXT}/${repoDir}" + + git clone -b "${ref}" "${repo}" \ + --depth 1 --single-branch --no-tags -c advice.detachedHead=false "${repoPath}" +} +github_url() { echo "https://github.com/$1.git"; } + +# Download external dependencies +mkdir -p "${DIR_SRCS_EXT}" + +# Download LLVM +LLVM_TAG="llvmorg-17.0.6" +curl -SL "https://github.com/llvm/llvm-project/archive/refs/tags/${LLVM_TAG}.tar.gz" \ + | tar -xzC "${DIR_SRCS_EXT}" +mv "${DIR_SRCS_EXT}/llvm-project-${LLVM_TAG}" "${DIR_SRCS_EXT}/llvm-project" + +# Download google-benchmark +GOOGLE_BENCHMARK_TAG="v1.8.4" +checkoutGitRepo "$(github_url google/benchmark)" "${GOOGLE_BENCHMARK_TAG}" "google-benchmark" + +# Download googletest +GOOGLETEST_TAG="v1.14.0" +checkoutGitRepo "$(github_url google/googletest)" "${GOOGLETEST_TAG}" "googletest" + +# Download zlib +ZLIB_TAG="v1.3.1" +checkoutGitRepo "$(github_url madler/zlib)" "${ZLIB_TAG}" "zlib" + +# Build libc++ with required instrumentation +# +# The extent to which all dependencies to be compiled with sanitizer-support +# varies by sanitizer. MemorySanitizer is especially unforgiving: Failing to +# link against an instrumented standard library will yield many false +# positives. Concensus is that compiling libc++ with `-fsanitize=memory` is a +# significantly easier endeavor than doing the same with libstdc++ (the gcc +# standard library). +# +# We therefore opt to use libc++ here, just to ensure maximum flexibility. We +# follow build instructions from https://libcxx.llvm.org/BuildingLibcxx.html +LIBCXX_SRC_PATH="${DIR_SRCS_EXT}/llvm-project/runtimes" +LIBCXX_BUILD_PATH="${LIBCXX_SRC_PATH}/cmake.bld" + +cmake -B "${LIBCXX_BUILD_PATH}" \ + -S "${LIBCXX_SRC_PATH}" \ + -DCMAKE_BUILD_TYPE="Debug" \ + -DCMAKE_C_COMPILER="clang" \ + -DCMAKE_CXX_COMPILER="clang++" \ + -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \ + -DLLVM_USE_SANITIZER="${LLVM_SANITIZER_NAME}" \ + "${LLVM_SPECIFIC_CMAKE_OPTIONS}" + +cmake --build "${LIBCXX_BUILD_PATH}" -j${PARALLELISM} --target cxx cxxabi unwind generate-cxx-headers + +# Variables read by our custom CMake toolchain used to build everything else. +export LIBCXX_BUILD_PATH="${LIBCXX_BUILD_PATH}" +export DIR_SRC_BMQ="${DIR_SRC_BMQ}" +export DIR_SCRIPTS="${DIR_SCRIPTS}" + +TOOLCHAIN_PATH="${DIR_SCRIPTS}/clang-libcxx-sanitizer.cmake" +export SANITIZER_NAME="${SANITIZER_NAME}" +export CC="clang" +export CXX="clang++" +export CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES="/usr/include;/usr/include/clang/${LLVM_VERSION}/include" +export BBS_BUILD_SYSTEM="ON" +PATH="$PATH:$(realpath "${DIR_SRCS_EXT}"/bde-tools/bin)" +export PATH + +# Build BDE + NTF +pushd "${DIR_SRCS_EXT}/bde" +eval "$(bbs_build_env -u dbg_64_safe_cpp20 -b "${DIR_BUILD_EXT}/bde")" +bbs_build configure --toolchain "${TOOLCHAIN_PATH}" +bbs_build build -j${PARALLELISM} +bbs_build --install=/opt/bb --prefix=/ install +popd + +pushd "${DIR_SRCS_EXT}/ntf-core" +# TODO The deprecated flag "-fcoroutines-ts" has been removed in clang +# 17.0.1, but NTF is still using it. We manually change this flag until +# the fix in issue 175307231 is resolved. +sed -i 's/fcoroutines-ts/fcoroutines/g' 'repository.cmake' + +./configure --keep \ + --prefix /opt/bb \ + --output "${DIR_BUILD_EXT}/ntf" \ + --without-warnings-as-errors \ + --without-usage-examples \ + --without-applications \ + --ufid 'dbg_64_safe_cpp20' \ + --toolchain "${TOOLCHAIN_PATH}" +make -j${PARALLELISM} +make install +popd + +# Note: Hack to circumvent faulty behavior in "nts-targets.cmake" +ln -sf "/opt/bb/include" "/opt/include" +ln -sf "/opt/bb/lib64" "/opt/lib64" + +# Setup CMake options for all remaining builds +CMAKE_OPTIONS=( \ + -D BUILD_BITNESS=64 \ + -D CMAKE_BUILD_TYPE=Debug \ + -D CMAKE_INSTALL_INCLUDEDIR=include \ + -D CMAKE_INSTALL_LIBDIR=lib64 \ + -D CMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_PATH}") + +# Build GoogleTest +cmake -B "${DIR_SRCS_EXT}/googletest/cmake.bld" \ + -S "${DIR_SRCS_EXT}/googletest" "${CMAKE_OPTIONS[@]}" \ + -DCMAKE_INSTALL_PREFIX=/opt/bb +cmake --build "${DIR_SRCS_EXT}/googletest/cmake.bld" -j${PARALLELISM} +cmake --install "${DIR_SRCS_EXT}/googletest/cmake.bld" --prefix "/opt/bb" + +# Build Google Benchmark +cmake -B "${DIR_SRCS_EXT}/google-benchmark/cmake.bld" \ + -S "${DIR_SRCS_EXT}/google-benchmark" "${CMAKE_OPTIONS[@]}" \ + -DCMAKE_INSTALL_PREFIX=/opt/bb \ + -DBENCHMARK_DOWNLOAD_DEPENDENCIES="ON" \ + -DBENCHMARK_ENABLE_GTEST_TESTS="false" \ + -DHAVE_STD_REGEX="ON" \ + -DBENCHMARK_ENABLE_TESTING="OFF" +cmake --build "${DIR_SRCS_EXT}/google-benchmark/cmake.bld" -j${PARALLELISM} +cmake --install "${DIR_SRCS_EXT}/google-benchmark/cmake.bld" --prefix "/opt/bb" + +# Build zlib +# Note: zlib has completely broken CMake install rules, so we must +# specify the install prefix *exactly* as it will be at configure +# time +# https://discourse.cmake.org/t/cmake-install-prefix-not-work/5040 +cmake -B "${DIR_SRCS_EXT}/zlib/cmake.bld" -S "${DIR_SRCS_EXT}/zlib" \ + -D CMAKE_INSTALL_PREFIX="/opt/bb" \ + "${CMAKE_OPTIONS[@]}" +# Make and install zlib. +cmake --build "${DIR_SRCS_EXT}/zlib/cmake.bld" -j${PARALLELISM} +cmake --install "${DIR_SRCS_EXT}/zlib/cmake.bld" + +# Build BlazingMQ +PKG_CONFIG_PATH="/opt/bb/lib64/pkgconfig:/opt/bb/lib/pkgconfig:/opt/bb/share/pkgconfig:$(pkg-config --variable pc_path pkg-config)" \ +cmake -B "${DIR_BUILD_BMQ}" -S "${DIR_SRC_BMQ}" -G Ninja \ + -DBDE_BUILD_TARGET_64=ON \ + -DBDE_BUILD_TARGET_CPP17=ON \ + -DCMAKE_PREFIX_PATH="${DIR_SRCS_EXT}/bde-tools/BdeBuildSystem" \ + -DBDE_BUILD_TARGET_SAFE=1 "${CMAKE_OPTIONS[@]}" +cmake --build "${DIR_BUILD_BMQ}" -j${PARALLELISM} \ + --target all.t -v --clean-first + +# Create testing script +envcfgquery() { + # Parses the '.environment' object from 'sanitizers.json', + # and outputs a string of whitespace-separated 'VAR=VAL' pairs intended to + # be used to set the environment for a command. + # e.g. 'asan' -> 'ASAN_OPTIONS="foo=bar:baz=baf" LSAN_OPTIONS="abc=fgh"' + # + cfgquery " \ + .${1}.environment | \ + to_entries | \ + map(\"\(.key)=\\\"\(.value | \ + to_entries | \ + map(\"\(.key)=\(.value)\") | \ + join(\":\"))\\\"\") | \ + join(\" \")" | + sed "s|%%SRC%%|$(realpath "${DIR_SRC_BMQ}")|g" | + sed "s|%%ROOT%%|$(realpath "${DIR_ROOT}")|g" +} + +mkscript() { + local cmd=${1} + local outfile=${2} + + echo '#!/usr/bin/env bash' > "${outfile}" + echo "${cmd}" >> "${outfile}" + chmod +x "${outfile}" +} + +SANITIZER_ENV="BMQ_BUILD=$(realpath "${DIR_BUILD_BMQ}") " +SANITIZER_ENV+="BMQ_REPO=${DIR_SRC_BMQ} " +SANITIZER_ENV+="$(envcfgquery "${SANITIZER_NAME}")" + +# 'run-env.sh' runs a command with environment required of the sanitizer. +mkscript "${SANITIZER_ENV} \${@}" "${DIR_BUILD_BMQ}/run-env.sh" + +# 'run-unittests.sh' runs all instrumented unit-tests. +CMD="cd $(realpath "${DIR_BUILD_BMQ}") && " +CMD+="./run-env.sh ctest -E mwcsys_executil.t --output-on-failure" +mkscript "${CMD}" "${DIR_BUILD_BMQ}/run-unittests.sh" diff --git a/.github/workflows/sanitizers/clang-libcxx-sanitizer.cmake b/.github/workflows/sanitizers/clang-libcxx-sanitizer.cmake new file mode 100644 index 0000000000..40cf97580c --- /dev/null +++ b/.github/workflows/sanitizers/clang-libcxx-sanitizer.cmake @@ -0,0 +1,121 @@ +# Compiler-less toolchain for building with {Clang, libc++} + Sanitizer. +# The actual compiler is passed via `CXX` and `CC` environment variables. +# Sanitizer name (asan/msan/tsan/ubsan) is passed via `SANITIZER_NAME` environment variable. + +cmake_minimum_required (VERSION 3.25) + +include("$ENV{DIR_SCRIPTS}/toolchain64.cmake") + +if(DEFINED ENV{CC}) + set(CMAKE_C_COMPILER $ENV{CC} CACHE STRING "Instrumentation C compiler" FORCE) +endif() + +if(DEFINED ENV{CXX}) + set(CMAKE_CXX_COMPILER $ENV{CXX} CACHE STRING "Instrumentation C++ compiler" FORCE) +endif() + +if(DEFINED ENV{CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES}) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES $ENV{CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES}) +endif() + +set(LIBCXX_BUILD_PATH "$ENV{LIBCXX_BUILD_PATH}") + +set(TOOLCHAIN_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG}") +set(TOOLCHAIN_C_FLAGS "${CMAKE_C_FLAGS_DEBUG}") + +# Build shared flags. +string(CONCAT TOOLCHAIN_SHARED_FLAGS + "${}" + "${TOOLCHAIN_SHARED_FLAGS} " + "-O0 " + "-g " + "-fno-omit-frame-pointer " + "-fno-optimize-sibling-calls " + "-fdiagnostics-show-option " + ) + +# Apply shared flags to each language. +string(CONCAT TOOLCHAIN_CXX_FLAGS + "${TOOLCHAIN_CXX_FLAGS} " + "${TOOLCHAIN_SHARED_FLAGS} " + ) +string(CONCAT TOOLCHAIN_C_FLAGS + "${TOOLCHAIN_C_FLAGS} " + "${TOOLCHAIN_SHARED_FLAGS} " + ) + +# Use libc++ standard library for C++. +string(CONCAT TOOLCHAIN_CXX_FLAGS + "${TOOLCHAIN_CXX_FLAGS} " + "-stdlib=libc++ " + "-I${LIBCXX_BUILD_PATH}/include/c++/v1 " + ) + +# Suppress some warnings when building C++ code. +string(CONCAT TOOLCHAIN_CXX_FLAGS + "${TOOLCHAIN_CXX_FLAGS} " + "-Wno-c++98-compat " + "-Wno-c++98-compat-extra-semi " + "-Wno-c++98-compat-pedantic " + "-Wno-deprecated " + "-Wno-deprecated-declarations " + "-Wno-disabled-macro-expansion " + "-Wno-extra-semi-stmt " + "-Wno-inconsistent-missing-destructor-override " + "-Wno-inconsistent-missing-override " + "-Wno-old-style-cast " + "-Wno-undef " + "-Wno-zero-as-null-pointer-constant " + ) + +# Define linker flags (used for both shared-objects and executables). +string( CONCAT TOOLCHAIN_LINKER_FLAGS + "${CMAKE_LINKER_FLAGS_DEBUG}" + "-stdlib=libc++ " + "-L${LIBCXX_BUILD_PATH}/lib " + "-Wl,-rpath,${LIBCXX_BUILD_PATH}/lib " + "-lc++abi " + ) + +set( TOOLCHAIN_EXE_FLAGS "${TOOLCHAIN_LINKER_FLAGS}" ) + +macro(set_build_type type) + set(CMAKE_CXX_FLAGS_${type} + "${TOOLCHAIN_CXX_FLAGS} ${TOOLCHAIN_${type}_FLAGS}" CACHE STRING "Default" FORCE) + set(CMAKE_C_FLAGS_${type} + "${TOOLCHAIN_C_FLAGS} ${TOOLCHAIN_${type}_FLAGS}" CACHE STRING "Default" FORCE) + set(CMAKE_EXE_LINKER_FLAGS_${type} + "${TOOLCHAIN_EXE_FLAGS} ${TOOLCHAIN_${type}_FLAGS} -static-libsan" CACHE STRING "Default" FORCE) + set(CMAKE_SHARED_LINKER_FLAGS_${type} + "${TOOLCHAIN_LINKER_FLAGS} ${TOOLCHAIN_${type}_FLAGS}" CACHE STRING "Default" FORCE) + set(CMAKE_MODULE_LINKER_FLAGS_${type} + "${TOOLCHAIN_LINKER_FLAGS} ${TOOLCHAIN_${type}_FLAGS}" CACHE STRING "Default" FORCE) +endmacro() + +# Define sanitizer specific flags +set(SANITIZER_NAME $ENV{SANITIZER_NAME}) +if(SANITIZER_NAME STREQUAL "asan") + set(TOOLCHAIN_DEBUG_FLAGS "-fsanitize=address ") +elseif(SANITIZER_NAME STREQUAL "msan") + set(MSAN_SUPPRESSION_LIST_PATH "$ENV{DIR_SRC_BMQ}/etc/msansup.txt") + set(TOOLCHAIN_DEBUG_FLAGS "-fsanitize=memory -fsanitize-blacklist=${MSAN_SUPPRESSION_LIST_PATH} ") + # Conditionally add flags helpful for debugging MemorySanitizer issues. + if (DEBUG_MEMORY_SANITIZER) + string(CONCAT TOOLCHAIN_DEBUG_FLAGS + "${TOOLCHAIN_DEBUG_FLAGS} " + "-fsanitize-memory-track-origins=2 " + ) + endif() +elseif(SANITIZER_NAME STREQUAL "tsan") + set(TOOLCHAIN_DEBUG_FLAGS "-fsanitize=thread ") +elseif(SANITIZER_NAME STREQUAL "ubsan") + set(TOOLCHAIN_DEBUG_FLAGS "-fsanitize=undefined ") +else() + message(FATAL_ERROR "Unexpected sanitizer name: ${SANITIZER_NAME}") +endif() + +# Set the final configuration variables, as understood by CMake. +set_build_type(DEBUG) + +# Disable GNU c++ extensions. +set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/.github/workflows/sanitizers/sanitizers.json b/.github/workflows/sanitizers/sanitizers.json new file mode 100644 index 0000000000..63e31dbcd2 --- /dev/null +++ b/.github/workflows/sanitizers/sanitizers.json @@ -0,0 +1,47 @@ +{ + "asan": { + "bde_compiler_name": "asan", + "llvm_sanitizer_name": "Address", + "environment": { + "ASAN_OPTIONS": { + "external_symbolizer_path": "/usr/bin/llvm-symbolizer" + }, + "LSAN_OPTIONS": { + "log_threads": 1, + "verbosity": 0 + } + } + }, + "msan": { + "bde_compiler_name": "msan", + "llvm_sanitizer_name": "MemoryWithOrigins", + "llvm_specific_cmake_options": "-DLIBCXXABI_USE_LLVM_UNWINDER=OFF", + "environment": { + "MSAN_OPTIONS": { + "external_symbolizer_path": "/usr/bin/llvm-symbolizer" + } + } + }, + "tsan": { + "bde_compiler_name": "tsan", + "llvm_sanitizer_name": "Thread", + "environment": { + "TSAN_OPTIONS": { + "external_symbolizer_path": "/usr/bin/llvm-symbolizer", + "second_deadlock_stack": 1, + "suppressions": "%%SRC%%/etc/tsansup.txt" + } + } + }, + "ubsan": { + "bde_compiler_name": "ubsan", + "llvm_sanitizer_name": "Undefined", + "environment": { + "UBSAN_OPTIONS": { + "external_symbolizer_path": "/usr/bin/llvm-symbolizer", + "suppressions": "%%SRC%%/etc/ubsansup.txt", + "print_stacktrace": "1" + } + } + } +} diff --git a/.github/workflows/sanitizers/toolchain64.cmake b/.github/workflows/sanitizers/toolchain64.cmake new file mode 100644 index 0000000000..45ddcd12a6 --- /dev/null +++ b/.github/workflows/sanitizers/toolchain64.cmake @@ -0,0 +1,33 @@ +# Configure compiler and ABI flags. +# This is a cmake toolchain file, designed to be specified by +# setting the CMAKE_TOOLCHAIN_FILE variable either as a command line parameter +# by using -D or by calling "set" before the top-level CMakeLists.txt call to +# project(). **This file should NOT be included directly**. The purpose +# of the toolchain file is to prepare the build directory for cross-compiling. + +cmake_minimum_required (VERSION 3.22) + +set(IS_64BIT yes CACHE BOOL "Tool chain bitness.") + +# Set the compiler flags. Use the CACHE instead of the *_INIT variants +# which are modified in the in the compiler initialization modules. +set(CMAKE_C_FLAGS "-march=westmere -m64 -fno-strict-aliasing" CACHE STRING "ABI C flags.") +set(CMAKE_CXX_STANDARD 20 CACHE STRING "C++ standard.") +set(CMAKE_CXX_STANDARD_REQUIRED 1 CACHE BOOL "Standard version is required.") +set(CMAKE_CXX_FLAGS "-D_GLIBCXX_USE_CXX11_ABI=0 -march=westmere -m64 -fno-strict-aliasing" CACHE STRING "ABI C++ flags.") + +# Compile variable overrides. +set(CMAKE_USER_MAKE_RULES_OVERRIDE ${CMAKE_CURRENT_LIST_FILE}) # Sets this file as the override for the builtin compiler variables and rules +set(CMAKE_C_LINK_FLAGS "-fasynchronous-unwind-tables -Wl,--enable-new-dtags -Wl,--gc-sections -Wl,-uplink_timestamp___") +set(CMAKE_CXX_LINK_FLAGS "-fasynchronous-unwind-tables -Wl,--enable-new-dtags -Wl,--gc-sections -Wl,-uplink_timestamp___") + +# Set the path for looking up includes, libs and files. +set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS yes) + +# Set the installation directory for libraries. This is for use as the +# DESTINATION for calls to the cmake 'install' command. Note that this is not +# an actual cmake variable, but has become a de facto standard for use in +# systems that employ a lib/lib64 deployment architecture. To allow for use +# without a toolchain, use "./${CMAKE_INSTALL_LIBDIR}" to avoid configuration +# errors when this variable is not set. +set(CMAKE_INSTALL_LIBDIR lib64) diff --git a/etc/tsansup.txt b/etc/tsansup.txt index ac2533e289..4056b7e308 100644 --- a/etc/tsansup.txt +++ b/etc/tsansup.txt @@ -28,6 +28,7 @@ race:BloombergLP::ball::LoggerManager::isInitialized() # It is a benign race in the test driver, but should be looked into at some # point. race:TestSession::waitForChannelClose +race:TestSession::arriveAtStepWithCfgs # Since we use mqbmock::Dispatcher in unit tests, this method does not get # enqueued correctly back to cluster-dispatcher thread, causing potential diff --git a/src/groups/mwc/mwcu/mwcu_sharedresource.t.cpp b/src/groups/mwc/mwcu/mwcu_sharedresource.t.cpp index 12ab445b49..99675a3f89 100644 --- a/src/groups/mwc/mwcu/mwcu_sharedresource.t.cpp +++ b/src/groups/mwc/mwcu/mwcu_sharedresource.t.cpp @@ -220,7 +220,24 @@ static void test1_resource_creators() } // 4. exception safety - { + // Current BDE version 4.8 has a memory leak which is already fixed + // (https://github.com/bloomberg/bde/commit/42950dfbcaf2c76cdabc266afacecee67da21a59) + // but not released yet. Skip address sanitizer check for BDE version less + // than 4.9. +#if BSL_VERSION < BSL_MAKE_VERSION(4, 9) +#if defined(__has_feature) // Clang-supported method for checking sanitizers. + static const bool skipTest = __has_feature(address_sanitizer); +#elif defined(__SANITIZE_ADDRESS__) + // GCC-supported macros for checking ASAN. + static const bool skipTest = true; +#else + static const bool skipTest = false; // Default to running the test. +#endif +#else + static const bool skipTest = false; // Default to running the test. +#endif + + if (!skipTest) { typedef mwcu::SharedResourceFactoryDeleter Deleter; @@ -504,12 +521,29 @@ static void test4_resource_reset() } // 5. exception safety - { + // Current BDE version 4.8 has a memory leak which is already fixed + // (https://github.com/bloomberg/bde/commit/42950dfbcaf2c76cdabc266afacecee67da21a59) + // but not released yet. Skip address sanitizer check for BDE version less + // than 4.9. +#if BSL_VERSION < BSL_MAKE_VERSION(4, 9) +#if defined(__has_feature) // Clang-supported method for checking sanitizers. + static const bool skipTest = __has_feature(address_sanitizer); +#elif defined(__SANITIZE_ADDRESS__) + // GCC-supported macros for checking ASAN. + static const bool skipTest = true; +#else + static const bool skipTest = false; // Default to running the test. +#endif +#else + static const bool skipTest = false; // Default to running the test. +#endif + + if (!skipTest) { typedef mwcu::SharedResourceFactoryDeleter Deleter; - // this allocator is used to allocate the shared state and will throw - // the second time it is used + // this allocator is used to allocate the shared state and will + // throw the second time it is used bslma::TestAllocator onceAlloc; onceAlloc.setAllocationLimit(1);