From ee138cb41e7e92be49620811bef2b5e27396774d Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Thu, 20 Aug 2026 02:24:26 -0700 Subject: [PATCH] test: check that an orphaned swap unit isn't resurrected by a reload Issue: https://github.com/systemd/systemd/issues/43442 --- test/units/TEST-17-UDEV.issue-43442.sh | 129 +++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 test/units/TEST-17-UDEV.issue-43442.sh diff --git a/test/units/TEST-17-UDEV.issue-43442.sh b/test/units/TEST-17-UDEV.issue-43442.sh new file mode 100755 index 0000000000..1d97a6a75f --- /dev/null +++ b/test/units/TEST-17-UDEV.issue-43442.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Issue: https://github.com/systemd/systemd/issues/43442 +# +# A daemon-reload must not resurrect a .swap unit whose device link has disappeared. +# +# systemd creates one .swap unit per udev device link of every device listed in /proc/swaps. When such a +# device link goes away (e.g. because it is not unique and udev repoints it at another partition) while the +# swap itself stays on, the unit named after the link is left behind: the next daemon-reload drops all units +# and rebuilds them, the enumeration from /proc/swaps no longer knows the link, and the unit is only +# resurrected by the deserialization, i.e. by name, with no fragment on disk. That used to leave it +# +# LoadState=not-found, ActiveState=active, What= + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +LOOPDEV="" +UDEV_RULE=/run/udev/rules.d/99-testsuite-orphan-swap.rules +DEVLINK=/dev/testsuite-orphan-swap +SWAP_UNIT="$(systemd-escape --path --suffix=swap "$DEVLINK")" +FRAGMENT="/run/systemd/system/$SWAP_UNIT" +# Not /tmp/, that's a tmpfs here: swapping onto a loop device backed by memory that can itself be swapped +# out is a deadlock waiting to happen. +WORKDIR="$(mktemp -d -p /var/tmp)" + +at_exit() ( + set +e + + [[ -n "$LOOPDEV" ]] && swapoff "$LOOPDEV" + + rm -f "$FRAGMENT" "$UDEV_RULE" + udevadm control --reload + + if [[ -z "$LOOPDEV" ]]; then + rm -rf "$WORKDIR" + else + timeout 30 udevadm trigger --settle --action change "$LOOPDEV" + + # Neither detach the loop device nor unlink its backing file while the swap is still on: the + # /proc/swaps entry outlives both, naming a device node that is gone, and leaks into the subtests + # that follow in the same boot. + if swapon --show=NAME --noheadings | grep -x "$LOOPDEV" >/dev/null; then + echo "$LOOPDEV is still swapped on, leaving it and $WORKDIR behind" >&2 + else + losetup -d "$LOOPDEV" + rm -rf "$WORKDIR" + fi + fi +) + +trap at_exit EXIT + +truncate -s 64M "$WORKDIR"/swap.img +LOOPDEV="$(losetup --show --find "$WORKDIR"/swap.img)" +mkswap "$LOOPDEV" + +# Give the loop device an extra device link, so that systemd creates a second .swap unit named after it +# once the swap is on. +mkdir -p /run/udev/rules.d +cat >"$UDEV_RULE" <"$FRAGMENT" <"$UDEV_RULE" </dev/null