Skip to content

Commit

Permalink
test: run dfuzzer against Polkit's D-Bus interface
Browse files Browse the repository at this point in the history
TBD
  • Loading branch information
mrc0mmand committed Nov 8, 2024
1 parent fdf9172 commit d588f81
Show file tree
Hide file tree
Showing 3 changed files with 413 additions and 0 deletions.
31 changes: 31 additions & 0 deletions test/integration/dfuzzer/main.fmf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
summary: Run dbus-broker under sanitizers
test: ./test.sh
require:
- clang
- compiler-rt
- coreutils
- dbus
- dbus-devel
- dfuzzer
- expat-devel
- gcc-c++
- gdb
- gettext-devel
- git
- glib2-devel
- glibc-devel
- gobject-introspection-devel
- gtk-doc
- libasan
- libubsan
- llvm
- meson
- meson
- pam-devel
- pkgconfig(duktape)
- python3-dbusmock
- systemd
- systemd-container
- systemd-devel
- util-linux
duration: 30m
167 changes: 167 additions & 0 deletions test/integration/dfuzzer/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/bin/bash
# vi: set sw=4 ts=4 et tw=110:
# shellcheck disable=SC2016
#
# FIXME: this test can be _drastically_ simplified once we can run a dedicated sanitizer job, see
# https://github.com/packit/packit-service/issues/2610

set -eux
set -o pipefail

# shellcheck source=test/integration/util.sh
. "$(dirname "$0")/../util.sh"

export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_invalid_pointer_pairs=2:handle_ioctl=1:print_cmdline=1:disable_coredump=0:use_madv_dontdump=1
export UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
# FIXME: There' a bug in meson where it overrides UBSAN_OPTIONS when MSAN_OPTIONS is not set, see
# https://github.com/mesonbuild/meson/pull/13001. Drop this once v1.4.1 is widespread enough
export MSAN_OPTIONS=foo
export CC="${CC:-clang}"

# shellcheck disable=SC2317
at_exit() {
set +ex

# Let's do some cleanup and export logs if necessary

# Collect potential coredumps
coredumpctl_collect
container_destroy

if [[ -n "${TMT_TEST_DATA:-}" && -n "${BUILD_DIR:-}" ]]; then
cp -r "$BUILD_DIR/meson-logs" "$TMT_TEST_DATA/"
fi
}

trap at_exit EXIT

export BUILD_DIR="$PWD/build-san"

# Make sure the coredump collecting machinery is working
coredumpctl_init

: "=== Prepare polkit's source tree ==="
# The integration test suite runs without access to the source tree it was built from. If we need the source
# tree (most likely to rebuild polkit) we need to do a little dance to determine the correct references.
if [[ -n "${PACKIT_TARGET_URL:-}" ]]; then
# If we're running in Packit's context, use the set of provided environment variables to checkout the
# correct branch (and possibly rebase it on top of the latest source base branch so we always test the
# latest revision possible).
git clone "$PACKIT_TARGET_URL" polkit
cd polkit
git checkout "$PACKIT_TARGET_BRANCH"
# If we're invoked from a pull request context, rebase on top of the latest source base branch.
if [[ -n "${PACKIT_SOURCE_URL:-}" ]]; then
git remote add pr "${PACKIT_SOURCE_URL:?}"
git fetch pr "${PACKIT_SOURCE_BRANCH:?}"
git merge "pr/$PACKIT_SOURCE_BRANCH"
fi
git log --oneline -5
elif [[ -n "${POLKIT_TREE:-}" ]]; then
# Useful for quick local debugging when running this script directly, e.g. running
#
# # TMT_TEST_DATA=$PWD/logs POLKIT_TREE=$PWD test/integration/fuzz/sanitizers/test.sh
#
# from the polkit repo root.
cd "${POLKIT_TREE:?}"
else
# If we're running outside of Packit's context, pull the latest polkit upstream.
git clone https://github.com/polkit-org/polkit polkit
cd polkit
git log --oneline -5
fi

: "=== Build polkit with sanitizers ==="
MESON_OPTIONS=()

if [[ "$CC" == clang ]]; then
# See https://github.com/mesonbuild/meson/issues/764 for details
MESON_OPTIONS+=(-Db_lundef=false)
fi

rm -rf "$BUILD_DIR"
# FIXME:
# - drop -Wno-deprecated-declarations once it's not needed
# - generating introspection is currently FUBAR when building with clang + ASan,
# but we shouldn't need it here anyway (see https://github.com/mesonbuild/meson/issues/13211)
meson setup "$BUILD_DIR" \
--werror \
-Dintrospection=false \
-Dsession_tracking=logind \
-Dgettext=true \
-Dtests=true \
-Db_sanitize=address,undefined \
-Dc_args="-Wno-deprecated-declarations" \
-Dcpp_args="-Wno-deprecated-declarations" \
"${MESON_OPTIONS[@]}"
ninja -C "$BUILD_DIR"
meson test -C "$BUILD_DIR" --print-errorlogs

: "=== Run dfuzzer against polkit running under sanitizers ==="
container_prepare

# Install our custom-built polkit into the container's overlay
DESTDIR="$CONTAINER_OVERLAY" ninja -C "$BUILD_DIR" install
# Pass $ASAN_OPTIONS and $UBSAN_OPTIONS to the polkit service in the container
mkdir -p "$CONTAINER_OVERLAY/etc/systemd/system/polkit.service.d/"
cat >"$CONTAINER_OVERLAY/etc/systemd/system/polkit.service.d/sanitizer-env.conf" <<EOF
[Service]
Environment=ASAN_OPTIONS=$ASAN_OPTIONS
Environment=UBSAN_OPTIONS=$UBSAN_OPTIONS
SystemCallFilter=
EOF

check_journal_for_sanitizer_errors() {
if journalctl -q -D "/var/log/journal/${CONTAINER_MACHINE_ID:?}" --grep "SUMMARY:.+Sanitizer"; then
# Dump all messages recorded for the polkit.service, as that's usually where the stack trace ends
# up. If that's not the case, the full container journal is exported on test exit anyway, so we'll
# still have everything we need to debug the fail further.
journalctl -q -D "/var/log/journal/${CONTAINER_MACHINE_ID:?}" -o short-monotonic --no-hostname -u polkit.service --no-pager
exit 1
fi
}

run_and_check() {
local run=(container_run)

if [[ "$1" == "--unpriv" ]]; then
run=(container_run_user testuser)
shift
fi

# Run the passed command in the container
"${run[@]}" "$@"
# Check if polkit is still running...
"${run[@]}" systemctl status --full --no-pager polkit.service
# ... and if it didn't generate any sanitizer errors
check_journal_for_sanitizer_errors
}

# Start the container and wait until it's fully booted up
container_start
container_run pkexec --version
container_run systemctl start polkit.service
container_run systemctl --no-pager status polkit.service
# Make _extra_ sure we're running the sanitized polkit with the correct environment
#
# Note: the check is not particularly nice, as libasan can be linked either statically or dynamically, so we
# can't just check ldd's output. Another option is using nm/objdump to check for ASan-specific functions, but
# that's also error prone. Instead, let's call each binary with ASan's "help" option, which produces output
# only if the target binary is built with (hopefully working) ASan.
container_run bash -xec 'ASAN_OPTIONS=help=1 /proc/$(systemctl show -P MainPID polkit.service)/exe -h 2>&1 >/dev/null | grep -q AddressSanitizer'
container_run systemctl show -p Environment polkit.service | grep -q ASAN_OPTIONS

# Now we should have a container ready for our shenanigans

# Fuzz polkit's own interface
run_and_check dfuzzer -v -n org.freedesktop.PolicyKit1
run_and_check --unpriv dfuzzer -v -n org.freedesktop.PolicyKit1

# Shut down the container and check for any sanitizer errors, since some of the errors can be detected only
# after we start shutting things down.
container_stop
check_journal_for_sanitizer_errors
# Also, check if polit didn't fail during the lifetime of the container
(! journalctl -q -D "/var/log/journal/$CONTAINER_MACHINE_ID" _PID=1 --grep "polkit.service.*Failed with result")

exit 0
Loading

0 comments on commit d588f81

Please sign in to comment.