#!/usr/bin/env bash # scripts/quality/sanitizers.sh # # Build the fast test suite with AddressSanitizer + UndefinedBehaviorSanitizer # and run it. Catches: # * use-after-free, double-free, heap-buffer-overflow (ASan) # * signed integer overflow, NaN propagation, alignment violations (UBSan) # * Eigen / CGAL template-induced UB that escapes the regular build # # Local-only (not in CI): the sanitizer build is ~3× slower and brittle # against system libraries. Run it before every release tag, after # touching any Newton/Hessian code, or when investigating intermittent # test failures. # # Usage: # bash scripts/quality/sanitizers.sh # default: ASan + UBSan # ASAN_OPTIONS=... UBSAN_OPTIONS=... bash scripts/quality/sanitizers.sh # # Exit codes: # 0 every test passes under sanitizer instrumentation # 1 a sanitizer report was triggered (test failure or runtime error) # 2 prerequisite missing (no clang/gcc with sanitizer support) set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$ROOT" BUILD_DIR="build-sanitizers" SAN_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -O1 -g" # Default ASan/UBSan runtime options — print stack on first error, # abort on first issue (so CI logs make the cause obvious). export ASAN_OPTIONS="${ASAN_OPTIONS:-detect_leaks=1:abort_on_error=1:print_stacktrace=1}" export UBSAN_OPTIONS="${UBSAN_OPTIONS:-print_stacktrace=1:halt_on_error=1}" echo "========================================" echo " Sanitizer build (ASan + UBSan)" echo " flags : $SAN_FLAGS" echo " ASAN : $ASAN_OPTIONS" echo " UBSAN : $UBSAN_OPTIONS" echo "========================================" # ── Pick a compiler with sanitizer support ────────────────────────────────── # Prefer clang (better diagnostics); fall back to gcc. CXX_BIN="" for cand in clang++-17 clang++-16 clang++-15 clang++ g++; do if command -v "$cand" >/dev/null 2>&1; then CXX_BIN="$cand" break fi done if [ -z "$CXX_BIN" ]; then echo "FAIL: no clang++ / g++ found in PATH" >&2 exit 2 fi echo "Using CXX = $CXX_BIN ($("$CXX_BIN" --version | head -1))" echo # ── Configure ──────────────────────────────────────────────────────────────── cmake -S code -B "$BUILD_DIR" \ -DCMAKE_CXX_COMPILER="$CXX_BIN" \ -DCMAKE_CXX_FLAGS="$SAN_FLAGS" \ -DCMAKE_EXE_LINKER_FLAGS="$SAN_FLAGS" \ -DCMAKE_BUILD_TYPE=Debug \ -Wno-dev # ── Build the fast (non-CGAL) tests only ──────────────────────────────────── # CGAL tests would 4–5× the build time under sanitizers and have a # higher false-positive surface (CGAL's expression-template trickery). # Use the fast suite as the sanitizer canary; full coverage of the CGAL # layer is covered by coverage.sh + the regular Release build. nice -n 19 cmake --build "$BUILD_DIR" --target conformallab_tests \ -j"$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 2)" # ── Run ────────────────────────────────────────────────────────────────────── cd "$BUILD_DIR" if ctest -E "^cgal\." --output-on-failure --output-junit san-results.xml; then cd "$ROOT" echo echo "OK: all sanitizer-instrumented tests passed." exit 0 else cd "$ROOT" echo echo "FAIL: sanitizer-instrumented tests reported issues." echo " See: $BUILD_DIR/Testing/Temporary/LastTest.log" exit 1 fi