summaryrefslogtreecommitdiffstats
path: root/test/units/testsuite-74.coredump.sh
blob: f48607c0db2ce9efcde17b9373ba51e26a537fad (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail

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

# Make sure the binary name fits into 15 characters
CORE_TEST_BIN="/tmp/test-dump"
CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
MAKE_DUMP_SCRIPT="/tmp/make-dump"
# Unset $PAGER so we don't have to use --no-pager everywhere
export PAGER=

at_exit() {
    rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT"
}

trap at_exit EXIT

if systemd-detect-virt -cq; then
    echo "Running in a container, skipping the systemd-coredump test..."
    exit 0
fi

# To make all coredump entries stored in system.journal.
journalctl --rotate

# Check that we're the ones to receive coredumps
sysctl kernel.core_pattern | grep systemd-coredump

# Prepare "fake" binaries for coredumps, so we can properly exercise
# the matching stuff too
cp -vf /bin/sleep "${CORE_TEST_BIN:?}"
cp -vf /bin/sleep "${CORE_TEST_UNPRIV_BIN:?}"
# Simple script that spawns given "fake" binary and then kills it with
# given signal
cat >"${MAKE_DUMP_SCRIPT:?}" <<\EOF
#!/bin/bash -ex

bin="${1:?}"
sig="${2:?}"

ulimit -c unlimited
"$bin" infinity &
pid=$!
# Sync with the "fake" binary, so we kill it once it's fully forked off,
# otherwise we might kill it during fork and kernel would then report
# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN).
# In this case, wait until the "fake" binary (sleep in this case) enters
# the "interruptible sleep" state, at which point it should be ready
# to be sacrificed.
for _ in {0..9}; do
    read -ra self_stat <"/proc/$pid/stat"
    [[ "${self_stat[2]}" == S ]] && break
    sleep .5
done
kill -s "$sig" "$pid"
# This should always fail
! wait "$pid"
EOF
chmod +x "$MAKE_DUMP_SCRIPT"

# Privileged stuff
[[ "$(id -u)" -eq 0 ]]
# Trigger a couple of coredumps
"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
# In the tests we store the coredumps in journals, so let's generate a couple
# with Storage=external as well
mkdir -p /run/systemd/coredump.conf.d/
printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
rm -fv /run/systemd/coredump.conf.d/99-external.conf
# Wait a bit for the coredumps to get processed
timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"

if cgroupfs_supports_user_xattrs; then
    # Make sure we can forward crashes back to containers
    CONTAINER="testsuite-74-container"

    mkdir -p "/var/lib/machines/$CONTAINER"
    mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
    # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
    # but we need the ASan-related overrides scattered across /etc
    cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" <<EOF
[Service]
ExecStart=
ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
                         --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
EOF
    systemctl daemon-reload

    [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60

    machinectl start "$CONTAINER"
    timeout "$TIMEOUT" bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"

    [[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
    machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
    systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGABRT"
    systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
    # Wait a bit for the coredumps to get processed
    timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done"

    rm -rf "/var/lib/machines/$CONTAINER"
fi

coredumpctl
SYSTEMD_LOG_LEVEL=debug coredumpctl
coredumpctl --help
coredumpctl --version
coredumpctl --no-pager --no-legend
coredumpctl --all
coredumpctl -1
coredumpctl -n 1
coredumpctl --reverse
coredumpctl -F COREDUMP_EXE
coredumpctl --json=short | jq
coredumpctl --json=pretty | jq
coredumpctl --json=off
coredumpctl --root=/
coredumpctl --directory=/var/log/journal
coredumpctl --file="/var/log/journal/$(</etc/machine-id)"/*.journal
coredumpctl --since=@0
coredumpctl --since=yesterday --until=tomorrow
# We should have a couple of externally stored coredumps
coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
rm -f /tmp/coredumpctl.out

coredumpctl info
coredumpctl info "$CORE_TEST_BIN"
coredumpctl info /foo /bar/ /baz "$CORE_TEST_BIN"
coredumpctl info "${CORE_TEST_BIN##*/}"
coredumpctl info foo bar baz "${CORE_TEST_BIN##*/}"
coredumpctl info COREDUMP_EXE="$CORE_TEST_BIN"
coredumpctl info COREDUMP_EXE=aaaaa COREDUMP_EXE= COREDUMP_EXE="$CORE_TEST_BIN"

coredumpctl debug --debugger=/bin/true "$CORE_TEST_BIN"
SYSTEMD_DEBUGGER=/bin/true coredumpctl debug "$CORE_TEST_BIN"
coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_BIN##*/}"

coredumpctl dump "$CORE_TEST_BIN" >/tmp/core.redirected
test -s /tmp/core.redirected
coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}"
test -s /tmp/core.output
rm -f /tmp/core.{output,redirected}

# Unprivileged stuff
# Related issue: https://github.com/systemd/systemd/issues/26912
UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --)
# Trigger a couple of coredumps as an unprivileged user
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
# In the tests we store the coredumps in journals, so let's generate a couple
# with Storage=external as well
mkdir -p /run/systemd/coredump.conf.d/
printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
rm -fv /run/systemd/coredump.conf.d/99-external.conf
# Wait a bit for the coredumps to get processed
timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"

# root should see coredumps from both binaries
coredumpctl info "$CORE_TEST_UNPRIV_BIN"
coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
# The test user should see only their own coredumps
"${UNPRIV_CMD[@]}" coredumpctl
"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN"
"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN")
(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}")
# We should have a couple of externally stored coredumps
"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
rm -f /tmp/coredumpctl.out

"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN"
"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}"

"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected
test -s /tmp/core.redirected
"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}"
test -s /tmp/core.output
rm -f /tmp/core.{output,redirected}
(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null)

# --backtrace mode
# Pass one of the existing journal coredump records to systemd-coredump and
# use our PID as the source to make matching the coredump later easier
# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME
journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
    /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine
# Wait a bit for the coredump to get processed
timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
coredumpctl info "$$"
coredumpctl info COREDUMP_HOSTNAME="mymachine"

# This used to cause a stack overflow
systemd-run -t --property CoredumpFilter=all ls /tmp
systemd-run -t --property CoredumpFilter=default ls /tmp

(! coredumpctl --hello-world)
(! coredumpctl -n 0)
(! coredumpctl -n -1)
(! coredumpctl --file=/dev/null)
(! coredumpctl --since=0)
(! coredumpctl --until='')
(! coredumpctl --since=today --until=yesterday)
(! coredumpctl --directory=/ --root=/)
(! coredumpctl --json=foo)
(! coredumpctl -F foo -F bar)
(! coredumpctl list 0)
(! coredumpctl list -- -1)
(! coredumpctl list '')
(! coredumpctl info /../.~=)
(! coredumpctl info '')
(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN")
(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN")
(! coredumpctl debug --debugger=/bin/false)
(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"')