From 3917534d620c2b358a196431b9e2593218ba1ac9 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Tue, 14 Jun 2022 22:54:39 +0200 Subject: test: wrap binaries using systemd DSOs when running w/ ASan Let's detect & wrap binaries which are linked against systemd DSOs and we're running under ASan, since otherwise running such binaries ends with: ``` ==633==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD. ``` --- test/test-functions | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'test/test-functions') diff --git a/test/test-functions b/test/test-functions index 71413624f9..67019acf43 100644 --- a/test/test-functions +++ b/test/test-functions @@ -2440,6 +2440,9 @@ inst_binary() { local file line local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)' + # DSOs provided by systemd + local systemd_so_regex='/(libudev|libsystemd.*|.+[\-_]systemd([\-_].+)?|libnss_(mymachines|myhostname|resolve)).so' + local wrap_binary=0 # I love bash! while read -r line; do [[ "$line" = 'not a dynamic executable' ]] && break @@ -2448,6 +2451,12 @@ inst_binary() { # by ldd attempting to use the unprefixed RPATH. [[ "$line" =~ libsystemd.*\ not\ found ]] && continue + # We're built with ASan and the target binary loads one of the systemd's + # DSOs, so we need to tweak the environment before executing the binary + if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$line" =~ $systemd_so_regex ]]; then + wrap_binary=1 + fi + if [[ "$line" =~ $so_regex ]]; then file="${BASH_REMATCH[1]}" [[ -e "${initdir}/$file" ]] && continue @@ -2463,7 +2472,36 @@ inst_binary() { exit 1 fi done < <(LC_ALL=C ldd "$bin" 2>/dev/null) - inst_simple "$bin" "$target" + + # Same as above, but we need to wrap certain libraries unconditionally + # + # login - dlopen()s (not only) systemd's PAM modules + # tar - called by machinectl in TEST-25 + if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$bin" =~ /(login|tar)$ ]]; then + wrap_binary=1 + fi + + if get_bool "$wrap_binary"; then + dinfo "Creating ASan-compatible wrapper for binary '$target'" + # Install the target binary with a ".orig" suffix + inst_simple "$bin" "${target}.orig" + # Create a simple shell wrapper in place of the target binary, which + # sets necessary ASan-related env variables and then exec()s the + # suffixed target binary + cat >"$initdir/$target" < Date: Wed, 15 Jun 2022 12:32:51 +0200 Subject: test: drop all LD_PRELOAD-related ASan workarounds since they shouldn't be necessary anymore, as we tweak the "problematic" binaries on per-binary basis. --- test/test-functions | 54 +---------------------------------------------------- 1 file changed, 1 insertion(+), 53 deletions(-) (limited to 'test/test-functions') diff --git a/test/test-functions b/test/test-functions index 67019acf43..01cc063e12 100644 --- a/test/test-functions +++ b/test/test-functions @@ -600,10 +600,6 @@ install_verity_minimal() { # missing $LD_PRELOAD libraries. inst_libs "$ASAN_RT_PATH" inst_library "$ASAN_RT_PATH" - # Create a dummy LSan suppression file otherwise gcc's ASan - # complains as it doesn't exist in the minimal image - # (i.e. when running TEST-29 or TEST-50 under sanitizers) - touch "$initdir/systemd-lsan.supp" fi cp "$os_release" "$initdir/usr/lib/os-release" ln -s ../usr/lib/os-release "$initdir/etc/os-release" @@ -808,20 +804,6 @@ if [[ ! -e "$ASAN_RT_PATH" ]]; then exit 1 fi -# Suppress certain leaks reported by LSan (either in external tools or bogus -# ones) -# Docs: # https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#suppressions -# -# - fsck is called by systemd-homed and is reporting a leak we're not interested -# in -# - libLLVM is a "side effect" caused by the previous fsck leak -cat >/systemd-lsan.supp </etc/systemd/system/systemd-journal-flush.service.d/timeout.conf -# D-Bus has troubles during system shutdown causing it to fail. Although it's -# harmless, it causes unnecessary noise in the logs, so let's disable LSan's -# at_exit check just for the dbus.service -mkdir -p /etc/systemd/system/dbus.service.d -printf "[Service]\nEnvironment=ASAN_OPTIONS=leak_check_at_exit=false\n" >/etc/systemd/system/dbus.service.d/disable-lsan.conf - -# Some utilities run via IMPORT/RUN/PROGRAM udev directives fail because -# they're uninstrumented (like dmsetup). Let's add a simple rule which sets -# LD_PRELOAD to the ASan RT library to fix this. -mkdir -p /etc/udev/rules.d -cat >/etc/udev/rules.d/00-set-LD_PRELOAD.rules <"\$_dropin_dir/unset_ld_preload.conf" -} - -unset_ld_preload systemd-remount-fs -unset_ld_preload testsuite- - export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS exec "$ROOTLIBDIR/systemd" "\$@" EOF -- cgit v1.2.1 From b727d7e02d6c88476ae9e46211e1f9c24720d5c3 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 15 Jun 2022 19:43:11 +0200 Subject: test: don't wrap binaries built with ASan since they should handle loading other instrumented libraries without issues. --- test/test-functions | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'test/test-functions') diff --git a/test/test-functions b/test/test-functions index 01cc063e12..6fe1e2762a 100644 --- a/test/test-functions +++ b/test/test-functions @@ -228,6 +228,8 @@ DEBUGTOOLS=( ) is_built_with_asan() { + local _bin="${1:?}" + if ! type -P objdump >/dev/null; then ddebug "Failed to find objdump. Assuming systemd hasn't been built with ASAN." return 1 @@ -235,7 +237,7 @@ is_built_with_asan() { # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182 local _asan_calls - _asan_calls="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "(callq?|brasl?|bl)\s.+__asan" -c)" + _asan_calls="$(objdump -dC "$_bin" | grep -E "(callq?|brasl?|bl)\s.+__asan" -c)" if ((_asan_calls < 1000)); then return 1 else @@ -251,7 +253,7 @@ is_built_with_coverage() { meson configure "${BUILD_DIR:?}" | grep 'b_coverage' | awk '{ print $2 }' | grep -q 'true' } -IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no) +IS_BUILT_WITH_ASAN=$(is_built_with_asan "$SYSTEMD_JOURNALD" && echo yes || echo no) IS_BUILT_WITH_COVERAGE=$(is_built_with_coverage && echo yes || echo no) if get_bool "$IS_BUILT_WITH_ASAN"; then @@ -2429,7 +2431,9 @@ inst_binary() { wrap_binary=1 fi - if get_bool "$wrap_binary"; then + # If the target binary is built with ASan support, we don't need to wrap + # it, as it should handle everything by itself + if get_bool "$wrap_binary" && ! is_built_with_asan "$bin"; then dinfo "Creating ASan-compatible wrapper for binary '$target'" # Install the target binary with a ".orig" suffix inst_simple "$bin" "${target}.orig" -- cgit v1.2.1