summaryrefslogtreecommitdiffstats
path: root/test/units/TEST-21-DFUZZER.sh
blob: 08ebfd91eb65996e10908dc46c53b7a8cfb7d384 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/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

# Save the end.service state before we start fuzzing, as it might get changed
# on the fly by one of the fuzzers
systemctl list-jobs | grep -F 'end.service' && SHUTDOWN_AT_EXIT=1 || SHUTDOWN_AT_EXIT=0

# shellcheck disable=SC2317
at_exit() {
    set +e
    # We have to call the end.service/poweroff explicitly even if it's specified on
    # the kernel cmdline via systemd.wants=end.service, since dfuzzer calls
    # org.freedesktop.systemd1.Manager.ClearJobs() which drops the service
    # from the queue
    if [[ $SHUTDOWN_AT_EXIT -ne 0 ]] && ! systemctl poweroff; then
        # PID1 is down let's try to save the journal
        journalctl --sync      # journal can be down as well so let's ignore exit codes here
        systemctl -ff poweroff # sync() and reboot(RB_POWER_OFF)
    fi
}

add_suppression() {
    local interface="${1:?}"
    local suppression="${2:?}"

    sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
}

trap at_exit EXIT

systemctl log-level info

# FIXME: systemd-run doesn't play well with daemon-reexec
# See: https://github.com/systemd/systemd/issues/27204
add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME"

add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive"
add_suppression "org.freedesktop.login1" "Sleep destructive"

# 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.
for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do
    add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
done

cat /etc/dfuzzer.conf

# TODO
#   * check for possibly newly introduced buses?
BUS_LIST=(
    org.freedesktop.home1
    org.freedesktop.hostname1
    org.freedesktop.import1
    org.freedesktop.locale1
    org.freedesktop.login1
    org.freedesktop.machine1
    org.freedesktop.portable1
    org.freedesktop.resolve1
    org.freedesktop.systemd1
    org.freedesktop.timedate1
)

# systemd-oomd requires PSI
if tail -n +1 /proc/pressure/{cpu,io,memory}; then
    BUS_LIST+=(
        org.freedesktop.oom1
    )
fi

# Some services require specific conditions:
#   - systemd-timesyncd can't run in a container
#   - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability
if ! systemd-detect-virt --container; then
    BUS_LIST+=(
        org.freedesktop.network1
        org.freedesktop.timesync1
    )
elif busctl introspect org.freedesktop.network1 / &>/dev/null; then
    BUS_LIST+=(
        org.freedesktop.network1
    )
fi

SESSION_BUS_LIST=(
    org.freedesktop.systemd1
)

# 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

# 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

# Fuzz both the system and the session buses (where applicable)
for bus in "${BUS_LIST[@]}"; do
    echo "Bus: $bus (system)"
    systemd-run --pipe --wait \
                -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"

    # Let's reload the systemd daemon to test (de)serialization as well
    systemctl daemon-reload
    # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
    systemctl daemon-reexec
done

umount /var/lib/machines

for bus in "${SESSION_BUS_LIST[@]}"; do
    echo "Bus: $bus (session)"
    systemd-run --machine 'testuser@.host' --user --pipe --wait \
                -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"

    # Let's reload the systemd user daemon to test (de)serialization as well
    systemctl --machine 'testuser@.host' --user daemon-reload
    # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
    systemctl --machine 'testuser@.host' --user daemon-reexec
done

touch /testok