#!/bin/sh #************************************************************************** #* * #* OCaml * #* * #* Damien Doligez, Xavier Leroy, projet Gallium, INRIA Paris * #* * #* Copyright 2018 Institut National de Recherche en Informatique et * #* en Automatique. * #* * #* All rights reserved. This file is distributed under the terms of * #* the GNU Lesser General Public License version 2.1, with the * #* special exception on linking described in the file LICENSE. * #* * #************************************************************************** # This script is run on Inria's continuous-integration servers to recompile # from scratch, adding more run-time checks ("sanitizers") to the C code, # and run the test suite. # In this context, it is necessary to skip a few tests whose behaviour # is modified by the instrumentation: export OCAMLTEST_SKIP_TESTS="tests/afl-instrumentation/afltest.ml \ tests/afl-instrumentation/afl-fuzz-test.ml \ tests/runtime-errors/stackoverflow.ml" jobs=-j8 make=make ######################################################################### # Print each command before its execution set -x # stop on error set -e # Tell gcc to use only ASCII in its diagnostic outputs. export LC_ALL=C # How to run the test suite if test -n "$jobs" && test -x /usr/bin/parallel; then export PARALLEL="$jobs $PARALLEL" run_testsuite="$make -C testsuite parallel" else run_testsuite="$make -C testsuite all" fi # Figure out which version of llvm/clang to use llvm_version=$(clang -dumpversion | cut -d . -f 1) clang=clang-${llvm_version} llvm_bin_dir=/usr/lib/llvm-${llvm_version}/bin # A tool that makes error backtraces nicer # Need to pick the one that matches clang's version and is named # "llvm-symbolizer" (/usr/bin/llvm-symbolizer-xx doesn't work, # that would be too easy) export ASAN_SYMBOLIZER_PATH=${llvm_bin_dir}/llvm-symbolizer export TSAN_SYMBOLIZER_PATH="$ASAN_SYMBOLIZER_PATH" ######################################################################### echo "======== clang ${llvm_version}, address sanitizer, UB sanitizer ========" git clean -q -f -d -x # These are the undefined behaviors we want to check # Others occur on purpose e.g. signed arithmetic overflow ubsan="\ bool,\ builtin,\ bounds,\ enum,\ nonnull-attribute,\ nullability,\ object-size,\ pointer-overflow,\ returns-nonnull-attribute,\ shift-exponent,\ unreachable" # Select address sanitizer and UB sanitizer, with trap-on-error behavior sanitizers="-fsanitize=address -fsanitize=$ubsan -fno-sanitize-recover=all" export UBSAN_OPTIONS="print_stacktrace=1" # Don't optimize too much to get better backtraces of errors CFLAGS="-Og -g -fno-omit-frame-pointer $sanitizers" LDFLAGS="$sanitizers -Og -g" CC=$clang # Test that UBSAN works cat >ubsan.c < #include int main(int argc, char **argv) { int x = 100; bool b; memcpy(&b, &x, sizeof(b)); return b; } EOF $CC $CFLAGS -c ubsan.c $CC $LDFLAGS ubsan.o -o ubsan ./ubsan && exit 2 test $? -eq 1 rm -f ubsan ubsan.o ubsan.c # Test that ASAN works cat >asan.c < int main(int argc, char **argv) { char* x = malloc(4); free(x); free(x); return x[argc]; } EOF $CC $CFLAGS -c asan.c $CC $LDFLAGS asan.o -o asan ./asan && exit 2 test $? -eq 1 rm -f asan asan.o asan.c ./configure \ CC="$CC" \ CFLAGS="$CFLAGS" \ LDFLAGS="$LDFLAGS" \ --disable-stdlib-manpages --enable-dependency-generation # Build the system. We want to check for memory leaks, hence # 1- force ocamlrun to free memory before exiting # 2- add an exception for ocamlyacc, which doesn't free memory #OCAMLRUNPARAM="c=1" \ #LSAN_OPTIONS="suppressions=$(pwd)/tools/ci/inria/sanitizers/lsan-suppr.txt" \ #make $jobs # TEMPORARY: cleanup-at-exit mode is broken in 5.0, so turn off leak # detection entirely ASAN_OPTIONS="detect_leaks=0,use_sigaltstack=0" make $jobs # Run the testsuite. # We deactivate leak detection for two reasons: # - The suppressed leak detections related to ocamlyacc mess up the # output of the tests and are reported as failures by ocamltest. # - The Ocaml runtime does not free the memory when a fatal error # occurs. # We already use sigaltstack for signal handling. Our use might # interact with ASAN's. Hence, we tell ASAN not to use it. ASAN_OPTIONS="detect_leaks=0,use_sigaltstack=0" $run_testsuite ######################################################################### # Run the testsuite with ThreadSanitizer support (--enable-tsan) enabled. # Initially intended to detect data races in OCaml programs and C stubs, it has # proved effective at also detecting races in the runtime (see #11040). echo "======== clang ${llvm_version}, thread sanitizer ========" git clean -q -f -d -x ./configure \ CC="$CC" \ --enable-tsan \ CPPFLAGS="-DTSAN_INSTRUMENT_ALL" \ --disable-stdlib-manpages --enable-dependency-generation # Build the system make $jobs # Run the testsuite. TSAN_OPTIONS="" $run_testsuite ######################################################################### # This is a failed attempt at using the memory sanitizer # (to detect reads from uninitialized memory). # Some alarms are reported that look like false positive # and are impossible to debug. # echo "======== clang ${llvm_version}, memory sanitizer ========" # git clean -q -f -d -x # # Use clang 6.0 # # Memory sanitizer doesn't like the static data generated by ocamlopt, # # hence build bytecode only # # Select memory sanitizer # # Don't optimize at all to get better backtraces of errors # ./configure \ # CC="$CC" \ # CFLAGS="-O0 -g -fno-omit-frame-pointer -fsanitize=memory" \ # LDFLAGS="-fsanitize=memory" \ # --disable-native-compiler # # A tool that makes error backtraces nicer # # Need to pick the one that matches clang-6.0 # export MSAN_SYMBOLIZER_PATH=/usr/lib/llvm-6.0/bin/llvm-symbolizer # # Build the system (bytecode only) and test # make $jobs # $run_testsuite