verity: Support status info about FEC repaired events

Kernel 6.19 will support additional info on dm-verity status
line that contains number of FEC successful repair events.

This patch adds support to libcryptsetup and veritysetup status command.

Ref. https://lore.kernel.org/dm-devel/074e1ecc-6690-1c22-0dba-454e191e1b6f@redhat.com/T/#m1af31c9c4263fe2b1fb58dee2fd7f0bdf045c463
This commit is contained in:
Milan Broz
2025-11-09 22:36:40 +01:00
parent 11a4fc6790
commit 8da66c3066
7 changed files with 92 additions and 10 deletions

View File

@@ -2117,6 +2117,18 @@ int crypt_header_is_detached(struct crypt_device *cd);
int crypt_get_verity_info(struct crypt_device *cd,
struct crypt_params_verity *vp);
/**
* Get FEC repaired block count for VERITY device.
*
* @param cd crypt device handle
* @param name verity device name
* @param repaired FEC repaired blocks
*
* @return @e 0 on success or negative errno value otherwise.
*/
int crypt_get_verity_repaired(struct crypt_device *cd, const char *name,
uint64_t *repaired);
/**
* Get device parameters for INTEGRITY device.
*

View File

@@ -195,3 +195,8 @@ CRYPTSETUP_2.8 {
crypt_get_old_volume_key_size;
crypt_format_inline;
} CRYPTSETUP_2.7;
CRYPTSETUP_2.9 {
global:
crypt_get_verity_repaired;
} CRYPTSETUP_2.8;

View File

@@ -1992,6 +1992,40 @@ int dm_status_verity_ok(struct crypt_device *cd, const char *name)
return r;
}
int dm_status_verity_repaired(struct crypt_device *cd, const char *name, uint64_t *repaired)
{
int r;
struct dm_info dmi;
char *status_line = NULL, *p;
uint64_t val64;
if (dm_init_context(cd, DM_VERITY))
return -ENOTSUP;
r = dm_status_dmi(name, &dmi, DM_VERITY_TARGET, &status_line);
dm_exit_context();
if (r < 0 || !status_line || !*status_line) {
free(status_line);
return r;
}
p = status_line + 1;
while (*p == ' ')
p++;
if (!*p || *p == '-' || sscanf(p, "%" PRIu64, &val64) != 1) {
free(status_line);
return -ENOTSUP;
}
log_dbg(cd, "Verity volume %s status is %s.", name, status_line ?: "");
if (repaired)
*repaired = val64;
free(status_line);
return 0;
}
int dm_status_integrity_failures(struct crypt_device *cd, const char *name, uint64_t *count)
{
int r;

View File

@@ -6790,6 +6790,16 @@ int crypt_get_verity_info(struct crypt_device *cd,
return 0;
}
int crypt_get_verity_repaired(struct crypt_device *cd, const char *name,
uint64_t *repaired)
{
if (!cd || !isVERITY(cd->type) || !name || !repaired)
return -EINVAL;
return dm_status_verity_repaired(cd, name, repaired);
}
int crypt_get_integrity_info(struct crypt_device *cd,
struct crypt_params_integrity *ip)
{

View File

@@ -205,6 +205,7 @@ int dm_status_device(struct crypt_device *cd, const char *name);
int dm_status_suspended(struct crypt_device *cd, const char *name);
int dm_status_verity_ok(struct crypt_device *cd, const char *name);
int dm_status_integrity_failures(struct crypt_device *cd, const char *name, uint64_t *count);
int dm_status_verity_repaired(struct crypt_device *cd, const char *name, uint64_t *repaired);
int dm_query_device(struct crypt_device *cd, const char *name,
uint64_t get_flags, struct crypt_dm_active_device *dmd);
int dm_device_deps(struct crypt_device *cd, const char *name, const char *prefix,

View File

@@ -333,6 +333,7 @@ static int action_status(void)
size_t root_hash_size;
unsigned path = 0;
int r = 0;
uint64_t repaired;
/* perhaps a path, not a dm device name */
if (strchr(action_argv[0], '/') && !stat(action_argv[0], &st))
@@ -415,6 +416,8 @@ static int action_status(void)
log_std(" FEC offset: %" PRIu64 " [512-byte units] (%" PRIu64 " [bytes])\n",
vp.fec_area_offset * vp.hash_block_size / SECTOR_SIZE, vp.fec_area_offset * vp.hash_block_size);
log_std(" FEC roots: %u\n", vp.fec_roots);
if (!crypt_get_verity_repaired(cd, action_argv[0], &repaired))
log_std(" FEC repaired: %" PRIu64 " [events]\n", repaired);
}
root_hash_size = crypt_get_volume_key_size(cd);

View File

@@ -241,6 +241,7 @@ corrupt_device() # $1 device, $2 device_size(in bytes), $3 #{corrupted_bytes}
# $1 data_device, $2 hash_device, $3 fec_device, $4 data/hash_block_size(in bytes),
# $5 data_size(in blocks), $6 device_size(in blocks), $7 hash_offset(in bytes),
# $8 fec_offset(in bytes), $9 fec_roots, ${10} corrupted_bytes, [${11} superblock(y/n), ${12} salt]
# NOTE: do not use fail() in this function, use RET code
check_fec()
{
INDEX=25
@@ -292,18 +293,32 @@ check_fec()
dd if=/dev/mapper/$DEV_NAME of=$IMG_TMP > /dev/null 2>&1
HASH_REPAIRED=$(sha256sum $IMG_TMP | cut -d' ' -f 1)
# If empty, status not supported
REPAIRED=$(dmsetup status $DEV_NAME |sed -e s/.*verity\ \[VC\]\ *//)
if [ -n "$REPAIRED" -a "$REPAIRED" != "-" ] ; then
echo -n "[EC events: $REPAIRED]"
else
REPAIRED=""
fi
$VERITYSETUP close $DEV_NAME
if [ "$HASH_ORIG" != "$HASH_REPAIRED" ]; then
echo -n "[kernel correction failed]"
$VERITYSETUP verify $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS >/dev/null 2>&1 && fail "Userspace verify should fail"
echo -n "[userspace verify failed]"
RET=1
if [ -n "$REPAIRED" ]; then
[ "$REPAIRED" -eq 0 ] || { RET=4; echo "FEC repaired events should be 0."; }
fi
echo -n "[kernel correction failed]"
$VERITYSETUP verify $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS >/dev/null 2>&1 && { RET=5; echo "Userspace verify should fail"; }
echo -n "[userspace verify failed]"
else
echo -n "[repaired in kernel]"
$VERITYSETUP verify $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS >/dev/null 2>&1 || fail "Userspace verify failed"
echo "[userspace verify][OK]"
RET=0
echo -n "[repaired in kernel]"
if [ -n "$REPAIRED" ]; then
[ "$REPAIRED" -gt 0 ] || { RET=4; echo "FEC repaired events should be greater than 0."; }
fi
$VERITYSETUP verify $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS >/dev/null 2>&1 || { RET=5; echo "Userspace verify failed"; }
echo "[userspace verify][OK]"
fi
rm $1 $2 $3 $IMG_TMP > /dev/null 2>&1
return $RET
@@ -531,10 +546,10 @@ if check_version 1 3; then
echo "Veritysetup [FEC tests]"
for INDEX in {1..4}; do
# in the first iteration check if we can use FEC (it can be compiled-out)
(check_fec $IMG $IMG $IMG 4096 30 150 163840 409600 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) )
(check_fec $IMG $IMG $IMG 4096 30 150 163840 409600 $(($RANDOM % 23 + 2)) $(($INDEX * 4)))
RET=$?
[ "$RET" -eq "3" ] && break
[ "$RET" -eq "0" ] || fail "FEC repair failed"
[ "$RET" -eq 3 ] && break
[ "$RET" -eq 0 ] || fail "FEC repair failed"
(check_fec $IMG $IMG $IMG 512 500 50000 2457600 4915200 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'n' $SALT) || fail "FEC repair failed"
(check_fec $IMG $IMG $IMG 512 500 50000 2457600 4915200 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'y' $SALT) || fail "FEC repair failed"
@@ -546,7 +561,9 @@ if check_version 1 3; then
(check_fec $IMG $IMG_HASH $FEC_DEV 512 2000 2000 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed"
(check_fec $IMG $IMG_HASH $FEC_DEV 1024 2000 2000 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed"
# this test should fail
(check_fec $IMG $IMG_HASH $FEC_DEV 4096 30 30 0 0 $(($RANDOM % 23 + 2)) $(($RANDOM % 200 + 200))) && fail "FEC repair must fail"
(check_fec $IMG $IMG_HASH $FEC_DEV 4096 30 30 0 0 $(($RANDOM % 23 + 2)) $(($RANDOM % 200 + 200)))
RET=$?
[ "$RET" -eq 1 ] || fail "FEC repair must fail"
echo "[OK]"
done
fi