From a5f819314ec1048d5a7f72415142187a831862bb Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Mon, 13 May 2024 23:14:19 +0200 Subject: [PATCH] WIP: run dbus-broker under Valgrind --- test/integration/fuzz/valgrind/main.fmf | 11 ++ test/integration/fuzz/valgrind/test.sh | 151 ++++++++++++++++++++++++ test/integration/util.sh | 7 ++ 3 files changed, 169 insertions(+) create mode 100644 test/integration/fuzz/valgrind/main.fmf create mode 100755 test/integration/fuzz/valgrind/test.sh diff --git a/test/integration/fuzz/valgrind/main.fmf b/test/integration/fuzz/valgrind/main.fmf new file mode 100644 index 00000000..2c4e1dd9 --- /dev/null +++ b/test/integration/fuzz/valgrind/main.fmf @@ -0,0 +1,11 @@ +summary: Concise summary describing what the test does +test: ./test.sh +require: + - dbus-broker + - dbus-broker-debuginfo + - dfuzzer + - systemd + - systemd-container + - util-linux + - valgrind +duration: 30m diff --git a/test/integration/fuzz/valgrind/test.sh b/test/integration/fuzz/valgrind/test.sh new file mode 100755 index 00000000..788f364f --- /dev/null +++ b/test/integration/fuzz/valgrind/test.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# vi: set sw=4 ts=4 et tw=110: +# shellcheck disable=SC2016 + +set -eux +set -o pipefail + +# shellcheck source=test/integration/util.sh +. "$(dirname "$0")/../../util.sh" + +# shellcheck disable=SC2317 +at_exit() { + set +ex + + # Let's do some cleanup and export logs if necessary + + # Collect potential coredumps + coredumpctl_collect + container_destroy +} + +trap at_exit EXIT + +# Make sure the coredump collecting machinery is working +coredumpctl_init + +# Run the Valgrind-fied dbus-broker in a ligthweight container, so we don't risk damage to the underlying test +# machine (see fuzz/sanitizers/test.sh for more detailed reasoning). +# +# Some Valgrind-specific notes: +# - Valgrind on Arch _requires_ working debuginfod servers, since it doesn't ship debuginfod packages, so +# the container needs to be booted up with --network-veth, and the system dbus-broker service now needs to +# depend on the network-online.target +# - also, for the debuginfod stuff to work correctly, the respective .cache directories in user homes (which +# need to exist) need to be writable +container_prepare + +# Build & install a custom dbus-broker revision into the overlay if $DBUS_BROKER_TREE is set (for a quick +# local debugging): +# +# # TMT_TEST_DATA=$PWD/logs DBUS_BROKER_TREE=$PWD test/integration/fuzz/valgrind/test.sh +# +if [[ -n "${DBUS_BROKER_TREE:-}" ]]; then + pushd "$DBUS_BROKER_TREE" + meson setup build-valgrind --wipe --prefix=/usr + DESTDIR="$CONTAINER_OVERLAY" ninja -C build-valgrind install + rm -rf build-valgrind + popd +fi + +# TODO: Use --exit-on-first-error=yes? Without it either dbus-broker or Valgrind doesn't propagate exit code +# (set via --error-exitcode=) from the child back to the parent process, so even if dbus-broker itself +# exits with 66 due to Valgrind errors, the parent process (dbus-broker-launch) still returns 0. +VALGRIND_CMD=( + valgrind + --tool=memcheck + --leak-check=full + --track-origins=yes + --trace-children=yes + --track-fds=yes + --error-exitcode=66 +) +# Verify the Valgrind cmdline (and Valgrind itself) +"${VALGRIND_CMD[@]}" true +# Override the dbus-broker service so it starts dbus-broker under Valgrind +mkdir -p "$CONTAINER_OVERLAY/etc/systemd/system/dbus-broker.service.d/" +cat >"$CONTAINER_OVERLAY/etc/systemd/system/dbus-broker.service.d/valgrind.conf" <"$CONTAINER_OVERLAY/etc/systemd/system/dbus-broker.service.d/debuginfod.conf" <"$CONTAINER_OVERLAY/etc/systemd/user/dbus-broker.service.d/valgrind.conf" <"$CONTAINER_OVERLAY/etc/dbus-1/system-local.conf" < + + root + +EOF + +run_and_check() { + local run=(container_run) + local unpriv=0 + + if [[ "$1" == "--unpriv" ]]; then + run=(container_run_user testuser) + unpriv=1 + shift + fi + + # Run the passed command in the container + "${run[@]}" "$@" + # Check if dbus-broker is still running... + "${run[@]}" systemctl status --full --no-pager dbus-broker.service + if [[ $unpriv -ne 0 ]]; then + # (check the user instance too, if applicable) + "${run[@]}" systemctl status --user --full --no-pager dbus-broker.service + fi +} + +# Start the container and wait until it's fully booted up +container_start +# Make sure we're running dbus-broker under Valgrind +container_run bash -xec '[[ $(readlink -f /proc/$(systemctl show -P MainPID dbus-broker.service)/exe) =~ valgrind ]]' +container_run_user testuser bash -xec '[[ $(readlink -f /proc/$(systemctl show --user -P MainPID dbus-broker.service)/exe) =~ valgrind ]]' +journalctl -D "/var/log/journal/${CONTAINER_MACHINE_ID:?}" -e -n 10 --no-pager + +# Now we should have a container ready for our shenanigans + +# Let's start with something simple and run dfuzzer on the org.freedesktop.DBus bus +run_and_check dfuzzer -v -n org.freedesktop.DBus +# TODO: fuzz systemd too (as we do in the sanitizers test) + +# Shut down the container and check for any errors, since some of the errors can be detected only after we +# start shutting things down. +container_stop +# Check if dbus-broker didn't fail during the lifetime of the container +(! journalctl -q -D "/var/log/journal/$CONTAINER_MACHINE_ID" _PID=1 --grep "dbus-broker.service.*Failed with result") +# Check error count in Valgrind messages +while read -r line; do + # TODO: potentially replace this with something less horrifying + if ! [[ "$line" =~ \==\ ERROR\ SUMMARY:\ 0\ errors ]]; then + journalctl -q -D "/var/log/journal/$CONTAINER_MACHINE_ID" -o short-monotonic --no-hostname -u dbus-broker --no-pager + exit 1 + fi +done < <(journalctl -q -D "/var/log/journal/$CONTAINER_MACHINE_ID" -o short-monotonic --no-hostname --grep "== ERROR SUMMARY:") + +exit 0 diff --git a/test/integration/util.sh b/test/integration/util.sh index 3fdd8a2c..e43fab26 100644 --- a/test/integration/util.sh +++ b/test/integration/util.sh @@ -64,6 +64,8 @@ ExecStart=systemd-nspawn --quiet --network-veth --keep-unit --machine=%i --boot --uuid=$CONTAINER_MACHINE_ID \ --hostname=$CONTAINER_NAME \ --overlay=/etc:$CONTAINER_OVERLAY/etc:/etc \ + --overlay=/home::/home \ + --overlay=/root::/root \ --overlay-ro=/usr:$CONTAINER_OVERLAY/usr:/usr EOF systemctl daemon-reload @@ -79,6 +81,11 @@ EOF mkdir -p "$CONTAINER_OVERLAY/etc/sysusers.d/" cat >"$CONTAINER_OVERLAY/etc/sysusers.d/testuser.conf" <"$CONTAINER_OVERLAY/etc/tmpfiles.d/testuser.conf" <