2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # shellcheck disable=SC2016
7 # shellcheck source=test/units/util.sh
8 .
"$(dirname "$0")"/util.sh
10 # Make sure the unit's exec context matches its configuration
11 # See: https://github.com/systemd/systemd/pull/29552
13 # Even though hidepid= was introduced in kernel 3.3, we support only
14 # the post 5.8 implementation that allows us to apply the option per-instance,
15 # instead of the whole namespace. To distinguish between these two implementations
16 # lets check if we can mount procfs with a named value (e.g. hidepid=off), since
17 # support for this was introduced in the same commit as the per-instance stuff
18 proc_supports_option
() {
22 proc_tmp
="$(mktemp -d)"
23 mount
-t proc
-o "$option" proc
"$proc_tmp" && ec
=0 || ec
=$?
24 mountpoint
-q "$proc_tmp" && umount
-q "$proc_tmp"
30 # In coverage builds we disable ProtectSystem= and ProtectHome= via a service.d
31 # dropin in /etc. This dropin has, unfortunately, higher priority than
32 # the transient stuff from systemd-run. Let's just skip the following tests
33 # in that case instead of complicating the test setup even more */
34 if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
35 systemd-run
--wait --pipe -p ProtectSystem
=yes \
36 bash
-xec "test ! -w /usr; test ! -w /boot; test -w /etc; test -w /var"
37 systemd-run
--wait --pipe -p ProtectSystem
=full \
38 bash
-xec "test ! -w /usr; test ! -w /boot; test ! -w /etc; test -w /var"
39 systemd-run
--wait --pipe -p ProtectSystem
=strict \
40 bash
-xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
41 systemd-run
--wait --pipe -p ProtectSystem
=no \
42 bash
-xec "test -w /; test -w /etc; test -w /var; test -w /dev; test -w /proc"
44 MARK
="$(mktemp /root/.exec-context.XXX)"
45 systemd-run
--wait --pipe -p ProtectHome
=yes \
46 bash
-xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test ! -e $MARK"
47 systemd-run
--wait --pipe -p ProtectHome
=read-only \
48 bash
-xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test -e $MARK"
49 systemd-run
--wait --pipe -p ProtectHome
=tmpfs \
50 bash
-xec "test -w /home; test -w /root; test -w /run/user; test ! -e $MARK"
51 systemd-run
--wait --pipe -p ProtectHome
=no \
52 bash
-xec "test -w /home; test -w /root; test -w /run/user; test -e $MARK"
56 if proc_supports_option
"hidepid=off"; then
57 systemd-run
--wait --pipe -p ProtectProc
=noaccess
-p User
=testuser \
58 bash
-xec 'test -e /proc/1; test ! -r /proc/1; test -r /proc/$$$$/comm'
59 systemd-run
--wait --pipe -p ProtectProc
=invisible
-p User
=testuser \
60 bash
-xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
61 systemd-run
--wait --pipe -p ProtectProc
=ptraceable
-p User
=testuser \
62 bash
-xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
63 systemd-run
--wait --pipe -p ProtectProc
=ptraceable
-p User
=testuser
-p AmbientCapabilities
=CAP_SYS_PTRACE \
64 bash
-xec 'test -r /proc/1; test -r /proc/$$$$/comm'
65 systemd-run
--wait --pipe -p ProtectProc
=default
-p User
=testuser \
66 bash
-xec 'test -r /proc/1; test -r /proc/$$$$/comm'
69 if proc_supports_option
"subset=pid"; then
70 systemd-run
--wait --pipe -p ProcSubset
=pid
-p User
=testuser \
71 bash
-xec "test -r /proc/1/comm; test ! -e /proc/cpuinfo"
72 systemd-run
--wait --pipe -p ProcSubset
=all
-p User
=testuser \
73 bash
-xec "test -r /proc/1/comm; test -r /proc/cpuinfo"
76 if ! systemd-detect-virt
-cq; then
77 systemd-run
--wait --pipe -p ProtectKernelLogs
=yes -p User
=testuser \
78 bash
-xec "test ! -r /dev/kmsg"
79 systemd-run
--wait --pipe -p ProtectKernelLogs
=no
-p User
=testuser \
80 bash
-xec "test -r /dev/kmsg"
83 systemd-run
--wait --pipe -p BindPaths
="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
84 bash
-xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
85 systemd-run
--wait --pipe -p BindReadOnlyPaths
="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
86 bash
-xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
88 # Check if we correctly serialize, deserialize, and set directives that
89 # have more complex internal handling
90 if ! systemd-detect-virt
-cq; then
91 # Funny detail: this originally used the underlying rootfs device, but that,
92 # for some reason, caused "divide error" in kernel, followed by a kernel panic
94 LODEV
="$(losetup --show -f "$TEMPFILE")"
95 ROOT_DEV_MAJ_MIN
="$(lsblk -nro MAJ:MIN "$LODEV")"
96 EXPECTED_IO_MAX
="$ROOT_DEV_MAJ_MIN rbps=1000 wbps=1000000000000 riops=2000000000 wiops=4000"
97 EXPECTED_IO_LATENCY
="$ROOT_DEV_MAJ_MIN target=69000"
98 SERVICE_NAME
="test-io-directives-$RANDOM.service"
99 CGROUP_PATH
="/sys/fs/cgroup/system.slice/$SERVICE_NAME"
103 # Throw in a couple of invalid entries just to test things out
104 -p IOReadBandwidthMax
="/foo/bar 1M"
105 -p IOReadBandwidthMax
="/foo/baz 1M"
106 -p IOReadBandwidthMax
="$LODEV 1M"
107 -p IOReadBandwidthMax
="$LODEV 1K"
108 -p IOWriteBandwidthMax
="$LODEV 1G"
109 -p IOWriteBandwidthMax
="$LODEV 1T"
110 -p IOReadIOPSMax
="$LODEV 2G"
111 -p IOWriteIOPSMax
="$LODEV 4K"
112 -p IODeviceLatencyTargetSec
="$LODEV 666ms"
113 -p IODeviceLatencyTargetSec
="/foo/bar 69ms"
114 -p IODeviceLatencyTargetSec
="$LODEV 69ms"
115 -p IOReadBandwidthMax
="/foo/bar 1M"
116 -p IOReadBandwidthMax
="/foo/baz 1M"
117 # TODO: IODeviceWeight= doesn't work on loop devices and virtual disks
118 -p IODeviceWeight
="$LODEV 999"
119 -p IODeviceWeight
="/foo/bar 999"
122 systemctl set-property system.slice IOAccounting
=yes
123 # io.latency not available by default on Debian stable
124 if [[ -e /sys
/fs
/cgroup
/system.slice
/io.latency
]]; then
125 systemd-run
--wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
126 bash
-xec "diff <(echo $EXPECTED_IO_MAX) $CGROUP_PATH/io.max; diff <(echo $EXPECTED_IO_LATENCY) $CGROUP_PATH/io.latency"
131 -p CPUSchedulingPolicy
=rr
# ID: 2
132 -p CPUSchedulingPolicy
=fifo
# ID: 1
133 -p CPUSchedulingPriority
=5 # Actual prio: 94 (99 - prio)
134 -p CPUSchedulingPriority
=10 # Actual prio: 89 (99 - prio)
137 systemd-run
--wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
138 bash
-xec 'grep -E "^policy\s*:\s*1$" /proc/self/sched; grep -E "^prio\s*:\s*89$" /proc/self/sched'
142 -p DevicePolicy
=closed
143 -p DevicePolicy
=strict
144 -p DeviceAllow
="char-mem rm" # Allow read & mknod for /dev/{null,zero,...}
145 -p DeviceAllow
="/dev/loop0 rw"
146 -p DeviceAllow
="/dev/loop0 w" # Allow write for /dev/loop0
147 # Everything else should be disallowed per the strict policy
150 systemd-run
--wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
151 bash
-xec 'test -r /dev/null; test ! -w /dev/null; test ! -r /dev/loop0; test -w /dev/loop0; test ! -r /dev/tty; test ! -w /dev/tty'
153 if ! systemctl
--version |
grep -qF -- "-BPF_FRAMEWORK"; then
157 -p SocketBindAllow
=1234
158 -p SocketBindAllow
=ipv4
:udp
:any
159 -p SocketBindAllow
=ipv6
:6666
160 # Everything but the last assignment is superfluous, but it still exercises
161 # the parsing machinery
163 -p SocketBindDeny
=1111
164 -p SocketBindDeny
=ipv4
:1111
165 -p SocketBindDeny
=ipv4
:any
166 -p SocketBindDeny
=ipv4
:tcp
:any
167 -p SocketBindDeny
=ipv4
:udp
:10000-11000
168 -p SocketBindDeny
=ipv6
:1111
169 -p SocketBindDeny
=any
172 # We should fail with EPERM when trying to bind to a socket not on the allow list
173 # (nc exits with 2 in that case)
174 systemd-run
--wait -p SuccessExitStatus
="1 2" --pipe "${ARGUMENTS[@]}" \
175 bash
-xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
176 systemd-run
--wait -p SuccessExitStatus
="1 2" --pipe "${ARGUMENTS[@]}" \
177 bash
-xec 'timeout 1s nc -l ::1 9999; exit 42'
178 systemd-run
--wait -p SuccessExitStatus
="1 2" --pipe "${ARGUMENTS[@]}" \
179 bash
-xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42'
180 systemd-run
--wait -p SuccessExitStatus
="1 2" --pipe "${ARGUMENTS[@]}" \
181 bash
-xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42'
182 # Consequently, we should succeed when binding to a socket on the allow list
183 # and keep listening on it until we're killed by `timeout` (EC 124)
184 systemd-run
--wait --pipe -p SuccessExitStatus
=124 "${ARGUMENTS[@]}" \
185 bash
-xec 'timeout 1s nc -4 -l 127.0.0.1 1234; exit 1'
186 systemd-run
--wait --pipe -p SuccessExitStatus
=124 "${ARGUMENTS[@]}" \
187 bash
-xec 'timeout 1s nc -4 -u -l 127.0.0.1 5678; exit 1'
188 systemd-run
--wait --pipe -p SuccessExitStatus
=124 "${ARGUMENTS[@]}" \
189 bash
-xec 'timeout 1s nc -6 -l ::1 1234; exit 1'
190 systemd-run
--wait --pipe -p SuccessExitStatus
=124 "${ARGUMENTS[@]}" \
191 bash
-xec 'timeout 1s nc -6 -l ::1 6666; exit 1'
198 # {Cache,Configuration,Logs,Runtime,State}Directory=
200 -p CacheDirectory
="foo/bar/baz"
201 -p CacheDirectory
="foo"
202 -p CacheDirectory
="context"
203 -p CacheDirectoryMode
="0123"
204 -p CacheDirectoryMode
="0666"
205 -p ConfigurationDirectory
="context/foo also_context/bar context/nested/baz"
206 -p ConfigurationDirectoryMode
="0400"
207 -p LogsDirectory
="context/foo"
209 -p LogsDirectory
="context/a/very/nested/logs/dir"
210 -p RuntimeDirectory
="context"
211 -p RuntimeDirectory
="also_context"
212 -p RuntimeDirectoryPreserve
=yes
213 -p StateDirectory
="context"
214 -p StateDirectory
="./././././././context context context"
215 -p StateDirectoryMode
="0000"
219 systemd-run
--wait --pipe "${ARGUMENTS[@]}" \
220 bash
-xec '[[ $CACHE_DIRECTORY == /var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz ]];
221 [[ $(stat -c "%a" ${CACHE_DIRECTORY##*:}) == 666 ]]'
222 systemd-run
--wait --pipe "${ARGUMENTS[@]}" \
223 bash
-xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz ]];
224 [[ $(stat -c "%a" ${CONFIGURATION_DIRECTORY##*:}) == 400 ]]'
225 systemd-run
--wait --pipe "${ARGUMENTS[@]}" \
226 bash
-xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
227 [[ $(stat -c "%a" ${LOGS_DIRECTORY##*:}) == 755 ]]'
228 systemd-run
--wait --pipe "${ARGUMENTS[@]}" \
229 bash
-xec '[[ $RUNTIME_DIRECTORY == /run/also_context:/run/context ]];
230 [[ $(stat -c "%a" ${RUNTIME_DIRECTORY##*:}) == 755 ]];
231 [[ $(stat -c "%a" ${RUNTIME_DIRECTORY%%:*}) == 755 ]]'
232 systemd-run
--wait --pipe "${ARGUMENTS[@]}" \
233 bash
-xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
235 rm -rf /var
/{cache
,lib
,log
}/context
/etc
/{also_
,}context
239 # Note: keep limits of LimitDATA= and LimitAS= unlimited, otherwise ASan (LSan)
240 # won't be able to mmap the shadow maps
243 -p LimitCPU
=10:15 # ulimit -t
244 -p LimitFSIZE
=96G
# ulimit -f
245 -p LimitDATA
=8T
:infinity
246 -p LimitDATA
=infinity
# ulimit -d
247 -p LimitSTACK
=8M
# ulimit -s
248 -p LimitCORE
=infinity
249 -p LimitCORE
=17M
# ulimit -c
250 -p LimitRSS
=27G
# ulimit -m
251 -p LimitNOFILE
=7:127 # ulimit -n
252 -p LimitAS
=infinity
# ulimit -v
254 -p LimitNPROC
=64:infinity
# ulimit -u
255 -p LimitMEMLOCK
=37M
# ulimit -l
256 -p LimitLOCKS
=19:1021 # ulimit -x
257 -p LimitSIGPENDING
=21 # ulimit -i
258 -p LimitMSGQUEUE
=666 # ulimit -q
259 -p LimitNICE
=4 # ulimit -e
260 -p LimitRTPRIO
=8 # ulimit -r
261 -p LimitRTTIME
=666666 # ulimit -R
263 # Do all the checks in one giant inline shell blob to avoid the overhead of spawning
264 # a new service for each check
266 # Note: ulimit shows storage-related values in 1024-byte increments*
267 # Note2: ulimit -R requires bash >= 5.1
269 # * in POSIX mode -c a -f options show values in 512-byte increments; let's hope
270 # we never run in the POSIX mode
271 systemd-run
--wait --pipe "${ARGUMENTS[@]}" \
272 bash
-xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024));
273 : CPU; [[ $(ulimit -St) -eq 10 ]]; [[ $(ulimit -Ht) -eq 15 ]];
274 : FSIZE; [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]];
275 : DATA; [[ $(ulimit -Sd) == unlimited ]]; [[ $(ulimit -Hd) == unlimited ]];
276 : STACK; [[ $(ulimit -Ss) -eq $((8 * MB)) ]]; [[ $(ulimit -Hs) -eq $((8 * MB)) ]];
277 : CORE; [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]];
278 : RSS; [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]];
279 : NOFILE; [[ $(ulimit -Sn) -eq 7 ]]; [[ $(ulimit -Hn) -eq 127 ]];
280 : AS; [[ $(ulimit -Sv) == unlimited ]]; [[ $(ulimit -Hv) == unlimited ]];
281 : NPROC; [[ $(ulimit -Su) -eq 64 ]]; [[ $(ulimit -Hu) == unlimited ]];
282 : MEMLOCK; [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]];
283 : LOCKS; [[ $(ulimit -Sx) -eq 19 ]]; [[ $(ulimit -Hx) -eq 1021 ]];
284 : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]]; [[ $(ulimit -Hi) -eq 21 ]];
285 : MSGQUEUE; [[ $(ulimit -Sq) -eq 666 ]]; [[ $(ulimit -Hq) -eq 666 ]];
286 : NICE; [[ $(ulimit -Se) -eq 4 ]]; [[ $(ulimit -He) -eq 4 ]];
287 : RTPRIO; [[ $(ulimit -Sr) -eq 8 ]]; [[ $(ulimit -Hr) -eq 8 ]];
289 : RTTIME; [[ $(ulimit -SR) -eq 666666 ]]; [[ $(ulimit -HR) -eq 666666 ]];'
291 # RestrictFileSystems=
293 # Note: running instrumented binaries requires at least /proc to be accessible, so let's
294 # skip the test when we're running under sanitizers
296 # Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files,
297 # since gcov can't access the restricted filesystem (as expected)
298 if [[ ! -v ASAN_OPTIONS
]] && systemctl
--version |
grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf
; then
299 ROOTFS
="$(df --output=fstype /usr/bin | sed --quiet 2p)"
300 systemd-run
--wait --pipe -p RestrictFileSystems
="" ls /
301 systemd-run
--wait --pipe -p RestrictFileSystems
="$ROOTFS foo bar" ls /
302 (! systemd-run
--wait --pipe -p RestrictFileSystems
="$ROOTFS" ls /proc
)
303 (! systemd-run
--wait --pipe -p GCOV_ERROR_LOG
=/dev
/null
-p RestrictFileSystems
="foo" ls /)
304 systemd-run
--wait --pipe -p RestrictFileSystems
="$ROOTFS foo bar baz proc" ls /proc
305 systemd-run
--wait --pipe -p RestrictFileSystems
="$ROOTFS @foo @basic-api" ls /proc
306 systemd-run
--wait --pipe -p RestrictFileSystems
="$ROOTFS @foo @basic-api" ls /sys
/fs
/cgroup
308 systemd-run
--wait --pipe -p RestrictFileSystems
="~" ls /
309 systemd-run
--wait --pipe -p RestrictFileSystems
="~proc" ls /
310 systemd-run
--wait --pipe -p RestrictFileSystems
="~@basic-api" ls /
311 (! systemd-run
--wait --pipe -p GCOV_ERROR_LOG
=/dev
/null
-p RestrictFileSystems
="~$ROOTFS" ls /)
312 (! systemd-run
--wait --pipe -p RestrictFileSystems
="~proc" ls /proc
)
313 (! systemd-run
--wait --pipe -p RestrictFileSystems
="~@basic-api" ls /proc
)
314 (! systemd-run
--wait --pipe -p RestrictFileSystems
="~proc foo @bar @basic-api" ls /proc
)
315 (! systemd-run
--wait --pipe -p RestrictFileSystems
="~proc foo @bar @basic-api" ls /sys
)
316 systemd-run
--wait --pipe -p RestrictFileSystems
="~proc devtmpfs sysfs" ls /
317 (! systemd-run
--wait --pipe -p RestrictFileSystems
="~proc devtmpfs sysfs" ls /proc
)
318 (! systemd-run
--wait --pipe -p RestrictFileSystems
="~proc devtmpfs sysfs" ls /dev
)
319 (! systemd-run
--wait --pipe -p RestrictFileSystems
="~proc devtmpfs sysfs" ls /sys
)
322 # Ensure that clean-up codepaths work correctly if activation ultimately fails
323 touch /run
/not-a-directory
326 chmod +x
/tmp
/root
/foo
327 (! systemd-run
--wait --pipe false
)
328 (! systemd-run
--wait --pipe --unit "test-dynamicuser-fail" -p DynamicUser
=yes -p WorkingDirectory
=/nonexistent true
)
329 (! systemd-run
--wait --pipe -p RuntimeDirectory
=not-a-directory true
)
330 (! systemd-run
--wait --pipe -p RootDirectory
=/tmp
/root this-shouldnt-exist
)
331 (! systemd-run
--wait --pipe -p RootDirectory
=/tmp
/root
/foo
)
332 (! systemd-run
--wait --pipe --service-type=oneshot
-p ExecStartPre
=-/foo
/bar
/baz
-p ExecStart
=-/foo
/bar
/baz
-p RootDirectory
=/tmp
/root
-- "- foo")