diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 77eb29ef..d886f583 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -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. * diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index 3a54431a..e7cfa089 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -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; diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index a306bc5b..69a677fe 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -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; diff --git a/lib/setup.c b/lib/setup.c index 61245c40..34520ff7 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -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) { diff --git a/lib/utils_dm.h b/lib/utils_dm.h index 4a558118..f76ff2f4 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -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, diff --git a/src/veritysetup.c b/src/veritysetup.c index d97c695c..8e666e3f 100644 --- a/src/veritysetup.c +++ b/src/veritysetup.c @@ -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); diff --git a/tests/verity-compat-test b/tests/verity-compat-test index 638f39f1..5fa70217 100755 --- a/tests/verity-compat-test +++ b/tests/verity-compat-test @@ -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