The test occasionally fails on GHA CI when formatting with xxhash64 because dm-integrity's crypto_alloc_shash() -> request_module() path flakily fails to load the algorithm: [ 29.172664] TEST-67-INTEGRITY.sh[447]: + for a in crc32c crc32 xxhash64 sha1 sha256 [ 29.172664] TEST-67-INTEGRITY.sh[447]: + [[ xxhash64 == crc32 ]] [ 29.172664] TEST-67-INTEGRITY.sh[447]: + test_one xxhash64 0 [ 29.172664] TEST-67-INTEGRITY.sh[447]: + integritysetup format /dev/loop0 --batch-mode -I xxhash64 '' [ 29.223383] TEST-67-INTEGRITY.sh[1220]: device-mapper: reload ioctl on temporary-cryptsetup-fa8bebe3-1d87-4796-91e8-abc02c487bb5 (254:0) failed: No such file or directory [ 29.226916] kernel: device-mapper: table: 254:0: integrity: Invalid internal hash (-ENOENT) [ 29.227415] kernel: device-mapper: ioctl: error adding target to table [ 29.231586] TEST-67-INTEGRITY.sh[1220]: Cannot format integrity for device /dev/loop0. Preload each algorithm's crypto module before use, and skip algorithms that are not registered in /proc/crypto. Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
144 lines
4.2 KiB
Bash
Executable File
144 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
set -euxo pipefail
|
|
|
|
DM_NAME="integrity_test"
|
|
DM_NODE="/dev/mapper/${DM_NAME}"
|
|
DM_SERVICE="systemd-integritysetup@${DM_NAME}.service"
|
|
FS_UUID="01234567-ffff-eeee-eeee-0123456789ab"
|
|
|
|
TMP_DIR=
|
|
LOOP=
|
|
|
|
cleanup() (
|
|
set +e
|
|
|
|
if [[ -n "${LOOP}" ]]; then
|
|
losetup -d "${LOOP}"
|
|
fi
|
|
|
|
if [[ -n "${TMP_DIR}" ]]; then
|
|
rm -rf "${TMP_DIR}"
|
|
fi
|
|
|
|
rm -rf /run/udev/rules.d/
|
|
udevadm control --reload
|
|
)
|
|
|
|
trap cleanup EXIT
|
|
|
|
udevadm settle
|
|
|
|
# Enable debugging logs for loop and dm block devices.
|
|
mkdir -p /run/udev/rules.d/
|
|
cat >/run/udev/rules.d/00-integrity-test.rules <<EOF
|
|
SUBSYSTEM=="block", KERNEL=="loop*|dm-*", OPTIONS="log_level=debug"
|
|
EOF
|
|
|
|
# FIXME:
|
|
# There is no ordering restriction between underlying loopback block devices and DM devices.
|
|
# Hence, we may get wrong device node symlinks. To workaround that issue, let's decrease the
|
|
# priority for loopback block devices.
|
|
cat >/run/udev/rules.d/99-priority.rules <<EOF
|
|
SUBSYSTEM=="block", KERNEL=="loop*", OPTIONS="link_priority=-200"
|
|
EOF
|
|
|
|
udevadm control --reload
|
|
|
|
TMP_DIR="$(mktemp -d -t -p / integrity.tmp.XXXXXX)"
|
|
truncate -s 64M "${TMP_DIR}/image"
|
|
truncate -s 64M "${TMP_DIR}/data"
|
|
LOOP="$(losetup --show -f "${TMP_DIR}/image")"
|
|
udevadm wait --timeout=30 --settle "${LOOP}"
|
|
|
|
test_cleanup() (
|
|
set +e
|
|
|
|
if [[ -e "/run/systemd/generator/${DM_SERVICE}" ]]; then
|
|
systemctl stop "${DM_SERVICE}"
|
|
elif [[ -e "${DM_NODE}" ]]; then
|
|
integritysetup close "${DM_NAME}"
|
|
fi
|
|
|
|
udevadm wait --timeout=30 --settle --removed "${DM_NODE}"
|
|
|
|
# Clear integritytab.
|
|
rm -f /etc/integritytab
|
|
|
|
# Make the generator to re-run.
|
|
systemctl daemon-reload
|
|
)
|
|
|
|
test_one() {
|
|
local algorithm="${1?}"
|
|
local separate_data="${2?}"
|
|
local data_option
|
|
|
|
trap test_cleanup RETURN
|
|
|
|
if [[ "${separate_data}" == 1 ]]; then
|
|
data_option="--data-device=${TMP_DIR}/data"
|
|
else
|
|
data_option=""
|
|
fi
|
|
|
|
integritysetup format "${LOOP}" --batch-mode -I "${algorithm}" "${data_option}"
|
|
integritysetup open -I "${algorithm}" "${LOOP}" "${DM_NAME}" "${data_option}"
|
|
udevadm wait --timeout=30 --settle "${DM_NODE}"
|
|
mkfs.ext4 -U "${FS_UUID}" "${DM_NODE}"
|
|
# Wait for synthetic events being processed.
|
|
udevadm settle --timeout=30
|
|
integritysetup close "${DM_NAME}"
|
|
udevadm wait --timeout=30 --settle --removed "${DM_NODE}"
|
|
|
|
# Create integritytab.
|
|
if [[ "${separate_data}" == 1 ]]; then
|
|
data_option=",data-device=${TMP_DIR}/data"
|
|
else
|
|
data_option=""
|
|
fi
|
|
cat >"/etc/integritytab" <<EOF
|
|
${DM_NAME} ${LOOP} - integrity-algorithm=${algorithm}${data_option}
|
|
EOF
|
|
|
|
# Make the generator to re-run.
|
|
systemctl daemon-reload
|
|
|
|
# Check for existence of the unit file.
|
|
[[ -e "/run/systemd/generator/${DM_SERVICE}" ]]
|
|
|
|
# Make sure we are in a consistent state, e.g. not already active before we start.
|
|
[[ "$(systemctl is-active "${DM_SERVICE}")" == inactive ]]
|
|
systemctl start "${DM_SERVICE}"
|
|
udevadm wait --timeout=30 --settle "${DM_NODE}"
|
|
|
|
# Check the signature on the FS to ensure we can retrieve it and that is matches.
|
|
[[ "$(blkid -U "${FS_UUID}")" == "${DM_NODE}" ]]
|
|
}
|
|
|
|
for a in crc32c crc32 xxhash64 sha1 sha256; do
|
|
# dm-integrity uses crypto_alloc_shash() which triggers request_module()
|
|
# for the underlying hash algorithm when needed. That auto-load has been
|
|
# observed to fail flakily in some test environments, leading to errors
|
|
# like:
|
|
# kernel: device-mapper: table: NNN:N: integrity: Invalid internal hash (-ENOENT)
|
|
# integritysetup: Cannot format integrity for device /dev/loopN.
|
|
# Try to load the kernel module ahead of time to avoid that. Failure is
|
|
# acceptable here: the algorithm might be built-in (no module to load) or
|
|
# genuinely unsupported, in which case the next check will skip it.
|
|
modprobe -q "crypto-$a" || :
|
|
|
|
# Some algorithms are not supported on certain platforms (e.g. crc32 is
|
|
# missing on Alpine/postmarketOS). Skip them at runtime to avoid spurious
|
|
# failures.
|
|
if ! grep -q -E "^name\s+: $a\$" /proc/crypto; then
|
|
echo "Algorithm '$a' is not supported on this system, skipping."
|
|
continue
|
|
fi
|
|
|
|
test_one "$a" 0
|
|
test_one "$a" 1
|
|
done
|
|
|
|
touch /testok
|