test: improve handling of ASan under clang

Running integration tests with ASan is somewhat tricky to begin with, as
we need to pre-load the ASan runtime DSO for certain services (like
dbus), otherwise they won't start or behave as expected. In case of gcc
this is pretty easy, as we need the runtime DSO during compilation, so
it's already present on the host system. For clang things get more
complicated, as ASan is compiled in statically by default, thus to
enable the necessary dynamic-ish behavior one needs to compile with
-shared-libasan and then correctly set LD_PRELOAD_PATH, as the runtime
libraries are not in a standard library path.
This commit is contained in:
Frantisek Sumsal 2019-05-24 22:35:52 +02:00
parent de26d715e3
commit 37ee8dc80f
1 changed files with 55 additions and 4 deletions

View File

@ -54,6 +54,30 @@ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan
QEMU_MEM="1536M"
QEMU_SMP=4
# We need to correctly distinguish between gcc's and clang's ASan DSOs.
if ldd $BUILD_DIR/systemd | grep -q libasan.so; then
ASAN_COMPILER=gcc
elif ldd $BUILD_DIR/systemd | grep -q libclang_rt.asan; then
ASAN_COMPILER=clang
# As clang's ASan DSO is usually in a non-standard path, let's check if
# the environment is set accordingly. If not, warn the user and exit.
# We're not setting the LD_LIBRARY_PATH automagically here, because
# user should encounter (and fix) the same issue when running the unit
# tests (meson test)
if ldd "$BUILD_DIR/systemd" | grep -q "libclang_rt.asan.*not found"; then
_asan_rt_name="$(ldd $BUILD_DIR/systemd | awk '/libclang_rt.asan/ {print $1; exit}')"
_asan_rt_path="$(find /usr/lib* /usr/local/lib* -type f -name "$_asan_rt_name" 2>/dev/null | sed 1q)"
echo >&2 "clang's ASan DSO ($_asan_rt_name) is not present in the runtime library path"
echo >&2 "Consider setting LD_LIBRARY_PATH=${_asan_rt_path%/*}"
exit 1
fi
else
echo >&2 "systemd is not linked against the ASan DSO"
echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
exit 1
fi
fi
function find_qemu_bin() {
@ -268,7 +292,7 @@ setup_basic_environment() {
install_depmod_files
generate_module_dependencies
if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
create_asan_wrapper
create_asan_wrapper
fi
}
@ -348,7 +372,24 @@ EOF
create_asan_wrapper() {
local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan
local _asan_rt_pattern
ddebug "Create $_asan_wrapper"
case "$ASAN_COMPILER" in
gcc)
_asan_rt_pattern="*libasan*"
;;
clang)
_asan_rt_pattern="libclang_rt.asan-*"
# Install llvm-symbolizer to generate useful reports
# See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
dracut_install "llvm-symbolizer"
;;
*)
dfail "Unsupported compiler: $ASAN_COMPILER"
exit 1
esac
cat >$_asan_wrapper <<EOF
#!/bin/bash
@ -358,18 +399,28 @@ DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:chec
DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS"
# As right now bash is the PID 1, we can't expect PATH to have a sane value.
# Let's make one to prevent unexpected "<bin> not found" issues in the future
export PATH="/sbin:/bin:/usr/sbin:/usr/bin"
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -o remount,rw /
PATH_TO_ASAN=\$(find / -name '*libasan*' | sed 1q)
PATH_TO_ASAN=\$(find / -name '$_asan_rt_pattern' | sed 1q)
if [[ "\$PATH_TO_ASAN" ]]; then
# A lot of services (most notably dbus) won't start without preloading libasan
# See https://github.com/systemd/systemd/issues/5004
DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=\$PATH_TO_ASAN"
# Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
# unnecessary for gcc & libasan, however, for clang this is crucial, as its
# runtime ASan DSO is in a non-standard (library) path.
echo \${PATH_TO_ASAN%/*} > /etc/ld.so.conf.d/asan-path-override.conf
ldconfig
fi
echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf
# ASAN and syscall filters aren't compatible with each other.
find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
@ -475,14 +526,14 @@ get_ldpath() {
install_missing_libraries() {
# install possible missing libraries
for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
LD_LIBRARY_PATH=$(get_ldpath $i) inst_libs $i
LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath $i)" inst_libs $i
done
}
create_empty_image() {
local _size=500
if [[ "$STRIP_BINARIES" = "no" ]]; then
_size=$((2*_size))
_size=$((4*_size))
fi
rm -f "$TESTDIR/rootdisk.img"
# Create the blank file to use as a root filesystem