TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out
[[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}"
UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
-EFI_MOUNT="$(bootctl -p 2>/dev/null || echo /boot)"
+EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)"
QEMU_MEM="${QEMU_MEM:-512M}"
if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
PATH_TO_INIT=$ROOTLIBDIR/systemd
-BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
-DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find"
+BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
+DEBUGTOOLS="df free ls stty ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find vi mv"
STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
STATEFILE="$STATEDIR/.testdir"
if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
STRIP_BINARIES=no
- SKIP_INITRD=yes
+ SKIP_INITRD="${SKIP_INITRD:-yes}"
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() {
# SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
# Either way, only use this version if we aren't running in KVM, because
# nested KVM is flaky still.
- if [ `systemd-detect-virt -v` != kvm ] ; then
+ if [[ $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
[ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
fi
default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
- default_arch_initrd=/boot/initramfs-linux.img
+ default_arch_initrd=/boot/initramfs-linux-fallback.img
default_suse_initrd=/boot/initrd-${KERNEL_VER}
if [[ ! "$INITRD" ]]; then
if [[ -e "$default_fedora_initrd" ]]; then
fi
fi
- [ "$QEMU_SMP" ] || QEMU_SMP=1
+ # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
+ # i.e. use the number of online CPUs on the host machine. If the nproc utility
+ # is not installed or there's some other error when calling it, fall back
+ # to the original value (QEMU_SMP=1).
+ if ! [ "$QEMU_SMP" ]; then
+ if ! QEMU_SMP=$(nproc); then
+ dwarn "nproc utility is not installed, falling back to QEMU_SMP=1"
+ QEMU_SMP=1
+ fi
+ fi
find_qemu_bin || return 1
exit 1
fi
-if [[ "$LOOKS_LIKE_SUSE" ]]; then
- PARAMS+="rd.hostonly=0"
-else
- PARAMS+="ro"
-fi
+ if [[ "$LOOKS_LIKE_SUSE" ]]; then
+ PARAMS+="rd.hostonly=0"
+ elif [[ "$LOOKS_LIKE_ARCH" ]]; then
+ PARAMS+="rw"
+ else
+ PARAMS+="ro"
+ fi
-KERNEL_APPEND="$PARAMS \
+ KERNEL_APPEND="$PARAMS \
root=/dev/sda1 \
raid=noautodetect \
loglevel=2 \
fi
# Let's use KVM if it is available, but let's avoid using nested KVM as that is still flaky
- if [ -c /dev/kvm -a `systemd-detect-virt -v` != kvm ]; then
+ if [[ -c /dev/kvm && $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
fi
install_depmod_files
generate_module_dependencies
if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
- create_asan_wrapper
+ create_asan_wrapper
fi
}
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
set -x
DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
-DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1
-DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS:halt_on_error=1"
+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/'
# But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
mkdir -p "\$JOURNALD_CONF_DIR"
-printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd-journald.asan.log\n" >"\$JOURNALD_CONF_DIR/env.conf"
+printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd-journald.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS:log_path=/systemd-journald.ubsan.log\n" >"\$JOURNALD_CONF_DIR/env.conf"
+
+# Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path
+# Let's try to catch them by redirecting stderr (and stdout just in case) to a file
+# See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821
+printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CONF_DIR/out.conf"
# 90s isn't enough for some services to finish when literally everything is run
# under ASan+UBSan in containers, which, in turn, are run in VMs.
mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
+# Let's override another hard-coded timeout that kicks in too early
+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
+
+# 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
+REMOUNTFS_CONF_DIR=/etc/systemd/system/systemd-remount-fs.service.d
+mkdir -p "\$REMOUNTFS_CONF_DIR"
+printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$REMOUNTFS_CONF_DIR/env.conf"
+
export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
exec $ROOTLIBDIR/systemd "\$@"
EOF
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
[ -b "$LOOPDEV" ] || return 1
echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
sfdisk "$LOOPDEV" <<EOF
-,$((_size-10))M
+,$((_size-50))M
,
EOF
ret=$(($ret+1))
fi
- journald_report=$(find "$root" -name "systemd-journald.asan.log*" -exec cat {} \;)
- if [[ ! -z "$journald_report" ]]; then
- printf "%s" "$journald_report"
+ journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)
+ if [[ ! -z "$journald_report" ]]; then
+ printf "%s\n" "$journald_report"
+ cat "$root/systemd-journald.out" || true
ret=$(($ret+1))
- fi
+ fi
- pids=$("$BUILD_DIR/journalctl" -D "$root/var/log/journal" | perl -alne 'print $1 if /\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/')
- if [[ ! -z "$pids" ]]; then
+ pids=$(
+ "$BUILD_DIR/journalctl" -D "$root/var/log/journal" | perl -alne '
+ BEGIN {
+ %services_to_ignore = (
+ "dbus-daemon" => undef,
+ );
+ }
+ print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
+ )
+ if [[ ! -z "$pids" ]]; then
ret=$(($ret+1))
for pid in $pids; do
"$BUILD_DIR/journalctl" -D "$root/var/log/journal" _PID=$pid --no-pager
done
- fi
+ fi
fi
return $ret
return 0
fi
ddebug "Strip binaries"
- find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | xargs strip --strip-unneeded | ddebug
+ find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | \
+ xargs strip --strip-unneeded |& \
+ grep -v 'file format not recognized' | \
+ ddebug
}
create_rc_local() {
install_debug_tools() {
[[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
+
+ if [[ $INTERACTIVE_DEBUG ]]; then
+ # Set default TERM from vt220 to linux, so at least basic key shortcuts work
+ local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
+ mkdir -p "$_getty_override"
+ echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
+
+ cat > "$initdir/etc/motd" << EOF
+To adjust the terminal size use:
+ export COLUMNS=xx
+ export LINES=yy
+or
+ stty cols xx rows yy
+EOF
+ fi
}
install_libnss() {
install_dbus() {
inst $ROOTLIBDIR/system/dbus.socket
- # Fedora rawhide replaced dbus.service with dbus-daemon.service
- if [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+ # Newer Fedora versions use dbus-broker by default. Let's install it is available.
+ if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
+ inst $ROOTLIBDIR/system/dbus-broker.service
+ inst_symlink /etc/systemd/system/dbus.service
+ inst /usr/bin/dbus-broker
+ inst /usr/bin/dbus-broker-launch
+ elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+ # Fedora rawhide replaced dbus.service with dbus-daemon.service
inst $ROOTLIBDIR/system/dbus-daemon.service
# Alias symlink
inst_symlink /etc/systemd/system/dbus.service
mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
- ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
+ # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set
+ [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
# make the testsuite the default target
ln -fs testsuite.target $initdir/etc/systemd/system/default.target
import_testdir() {
[[ -e $STATEFILE ]] && . $STATEFILE
- if [[ -z "$TESTDIR" ]] || [[ ! -d "$TESTDIR" ]]; then
- TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
+ if [[ ! -d "$TESTDIR" ]]; then
+ if [[ -z "$TESTDIR" ]]; then
+ TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
+ else
+ mkdir -p "$TESTDIR"
+ fi
+
echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
export TESTDIR
fi
else
dwarn "can't run systemd-nspawn, skipping"
fi
- fi
+ fi
fi
return 0
}
exit 0
fi
-# Detect lib paths
+ # Detect lib paths
[[ $libdir ]] || for libdir in /lib64 /lib; do
[[ -d $libdir ]] && libdirs+=" $libdir" && break
done