Merge pull request #23741 from mrc0mmand/more-asan-tweaks
test: wrap certain uninstrumented binaries to make them work w/ ASan
This commit is contained in:
+46
-56
@@ -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
|
||||
@@ -600,10 +602,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"
|
||||
@@ -810,20 +808,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 <<INNER_EOF
|
||||
leak:/bin/fsck$
|
||||
leak:/sbin/fsck$
|
||||
leak:/lib/libLLVM
|
||||
INNER_EOF
|
||||
|
||||
DEFAULT_LSAN_OPTIONS=${LSAN_OPTIONS:-}:suppressions=/systemd-lsan.supp
|
||||
DEFAULT_ASAN_OPTIONS=${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}
|
||||
DEFAULT_UBSAN_OPTIONS=${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}
|
||||
DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS LSAN_OPTIONS=\$DEFAULT_LSAN_OPTIONS"
|
||||
@@ -836,9 +820,7 @@ mount -t proc proc /proc
|
||||
mount -t sysfs sysfs /sys
|
||||
mount -o remount,rw /
|
||||
|
||||
# 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=$ASAN_RT_PATH ASAN_RT_PATH=$ASAN_RT_PATH"
|
||||
DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT ASAN_RT_PATH=$ASAN_RT_PATH"
|
||||
|
||||
if [[ "$ASAN_COMPILER" == "clang" ]]; then
|
||||
# Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
|
||||
@@ -875,38 +857,6 @@ printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=240s\
|
||||
mkdir -p /etc/systemd/system/systemd-journal-flush.service.d
|
||||
printf "[Service]\nTimeoutSec=180s\n" >/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 <<INNER_EOF
|
||||
SUBSYSTEM=="block", ENV{LD_PRELOAD}="$ASAN_RT_PATH"
|
||||
INNER_EOF
|
||||
chmod 0644 /etc/udev/rules.d/00-set-LD_PRELOAD.rules
|
||||
|
||||
# The 'mount' utility doesn't behave well under libasan, causing unexpected
|
||||
# fails during boot and subsequent test results check:
|
||||
# bash-5.0# mount -o remount,rw -v /
|
||||
# mount: /dev/sda1 mounted on /.
|
||||
# bash-5.0# echo \$?
|
||||
# 1
|
||||
# Let's workaround this by clearing the previously set LD_PRELOAD env variable,
|
||||
# so the libasan library is not loaded for this particular service
|
||||
unset_ld_preload() {
|
||||
local _dropin_dir="/etc/systemd/system/\$1.service.d"
|
||||
mkdir -p "\$_dropin_dir"
|
||||
printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$_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
|
||||
@@ -2499,6 +2449,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
|
||||
@@ -2507,6 +2460,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
|
||||
@@ -2522,7 +2481,38 @@ 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 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"
|
||||
# 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" <<EOF
|
||||
#!/bin/bash
|
||||
# Preload the ASan runtime DSO, otherwise ASAn will complain
|
||||
export LD_PRELOAD="$ASAN_RT_PATH"
|
||||
# Disable LSan to speed things up, since we don't care about leak reports
|
||||
# from 'external' binaries
|
||||
export ASAN_OPTIONS=detect_leaks=0
|
||||
# Set argv[0] to the original binary name without the ".orig" suffix
|
||||
exec -a "\$0" -- "${target}.orig" "\$@"
|
||||
EOF
|
||||
chmod +x "$initdir/$target"
|
||||
else
|
||||
inst_simple "$bin" "$target"
|
||||
fi
|
||||
}
|
||||
|
||||
# same as above, except for shell scripts.
|
||||
|
||||
@@ -4,11 +4,12 @@ set -ex
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
|
||||
|
||||
# Prepare fresh disk image
|
||||
img="/var/tmp/test.img"
|
||||
dd if=/dev/zero of=$img bs=1024k count=20 status=none
|
||||
echo -n passphrase >/tmp/passphrase
|
||||
LD_PRELOAD="${ASAN_RT_PATH:-}" cryptsetup luksFormat -q --use-urandom $img /tmp/passphrase
|
||||
cryptsetup luksFormat -q --use-urandom $img /tmp/passphrase
|
||||
|
||||
# Enroll unlock with default PCR policy
|
||||
env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto $img
|
||||
|
||||
Reference in New Issue
Block a user