Files
systemd/test/units/TEST-21-DFUZZER.sh
Zbigniew Jędrzejewski-Szmek e1485c0546 tests: drop pointless redirection of stderr from 'command -v'
The whole point of using 'command -v' is that it's a very portable
way to check if an executable exists. It doesn't print an error.
2025-09-05 08:29:52 +02:00

225 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# check dfuzzer is present before testing
if ! command -v dfuzzer >/dev/null; then
echo "dfuzzer is not installed, skipping" | tee --append /skipped
exit 77
fi
if [[ ! -v ASAN_OPTIONS && ! -v UBSAN_OPTIONS && "${TEST_RUN_DFUZZER:-0}" == "0" ]]; then
echo "no sanitizer is enabled, skipping. (Hint: set TEST_RUN_DFUZZER=1 to run test forcibly)."
exit 77
fi
add_suppression() {
local interface="${1:?}"
local suppression="${2:?}"
sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
}
systemctl log-level info
# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also
# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some
# degree) by the respective method counterparts on the manager object.
MANAGER_METHOD_FILTER=(
StartUnit
StartUnitWithFlags
StartUnitReplace
StopUnit
RestartUnit
TryRestartUnit
ReloadOrRestartUnit
ReloadOrTryRestartUnit
KillUnit
QueueSignalUnit
FreezeUnit
AttachProcessesToUnit
RemoveSubgroupFromUnit
AbandonScope
CancelJob
Exit
Reboot
SoftReboot
PowerOff
Halt
KExec
SwitchRoot
EnqueueMarkedJobs
)
UNIT_METHOD_FILTER=(
Start
Stop
Restart
TryRestart
ReloadOrRestart
ReloadOrTryRestart
Kill
QueueSignal
Freeze
)
SCOPE_METHOD_FILTER=(
Abandon
)
JOB_METHOD_FILTER=(
Cancel
)
LOGIN_METHOD_FILTER=(
FlushDevices # This triggers all devices and makes the system super heavy
PowerOff
PowerOffWithFlags
Reboot
RebootWithFlags
Halt
HaltWithFlags
Suspend
SuspendWithFlags
Hibernate
HibernateWithFlags
HybridSleep
HybridSleepWithFlags
SuspendThenHibernate
SuspendThenHibernateWithFlags
ScheduleShutdown
)
for method in "${MANAGER_METHOD_FILTER[@]}"; do
add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:$method"
done
for method in "${UNIT_METHOD_FILTER[@]}"; do
add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
done
for method in "${SCOPE_METHOD_FILTER[@]}"; do
add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Scope:$method"
done
for method in "${JOB_METHOD_FILTER[@]}"; do
add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Job:$method"
done
for method in "${LOGIN_METHOD_FILTER[@]}"; do
add_suppression "org.freedesktop.login1" "org.freedesktop.login1.Manager:$method"
done
cat /etc/dfuzzer.conf
# TODO
# * check for possibly newly introduced buses?
NAME_LIST=(
home
hostname
import
locale
login
machine
portable
resolve
timedate
)
# Some services require specific conditions:
# - systemd-oomd requires PSI
# - systemd-timesyncd can't run in a container
# - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability
if tail -n +1 /proc/pressure/{cpu,io,memory}; then
NAME_LIST+=( oom )
fi
if ! systemd-detect-virt --container; then
NAME_LIST+=( timesync )
fi
if ip link add dummy-fuzz type dummy; then
# if a dummy interface is created, then let's also setup it for resolved
ip link set dummy-fuzz up
ip address add 192.0.2.1/24 dev dummy-fuzz
# When we can create a dummy interface, we definitely have CAP_NET_ADMIN
NAME_LIST+=( network )
# Create unit files for another dummy interface for networkd
mkdir -p /run/systemd/network
cat >/run/systemd/network/10-dummy-fuzz2.netdev <<EOF
[NetDev]
Kind=dummy
Name=dummy-fuzz2
EOF
cat >/run/systemd/network/10-dummy-fuzz2.network <<EOF
[Match]
Name=dummy-fuzz2
[Network]
Address=192.0.2.2/24
EOF
fi
# Maximum payload size generated by dfuzzer (in bytes) - default: 50K
PAYLOAD_MAX=50000
# Tweak the maximum payload size if we're running under sanitizers, since
# with larger payloads we start hitting reply timeouts
if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
PAYLOAD_MAX=10000 # 10K
fi
# Disable debugging logs from systemd-homed, systemd-nsresourced, and systemd-userdbd.
# Otherwise, journal is filled with the debugging logs by them.
systemctl service-log-level systemd-homed.service info
for service in systemd-nsresourced.service systemd-userdbd.service; do
mkdir -p "/run/systemd/system/${service}.d"
cat >"/run/systemd/system/${service}.d/10-disable-debug.conf" <<EOF
[Service]
Environment=SYSTEMD_LOG_LEVEL=info
EOF
systemctl daemon-reload
systemctl restart "$service"
done
test_systemd() {
systemd-run "$@" --pipe --wait \
-- dfuzzer -b "$PAYLOAD_MAX" -n org.freedesktop.systemd1
# Let's reload the systemd user daemon to test (de)serialization as well
systemctl "$@" daemon-reload
# FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
systemctl "$@" daemon-reexec
}
# Let's first test the session bus before the system one, as it may be in a
# spurious state after fuzzing the system bus or login bus.
echo "Bus: org.freedesktop.systemd1 (session)"
test_systemd --machine 'testuser@.host' --user
# Overmount /var/lib/machines with a size-limited tmpfs, as fuzzing
# the org.freedesktop.machine1 stuff makes quite a mess
mount -t tmpfs -o size=50M tmpfs /var/lib/machines
# Next, test the system service buses, as the services may be in a spurious
# state after fuzzing the system service manager bus.
for name in "${NAME_LIST[@]}"; do
bus="org.freedesktop.${name}1"
service="systemd-${name}d.service"
echo "Bus: $bus"
# Unmask and enable the service.
systemctl unmask "$service"
systemctl enable "$service"
# enable debugging logs
systemctl service-log-level "$service" debug || :
systemd-run --pipe --wait \
-- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
# disable debugging logs
systemctl service-log-level "$service" info || :
done
umount /var/lib/machines
# Finally, test the system bus.
echo "Bus: org.freedesktop.systemd1 (system)"
test_systemd
touch /testok