mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-17 22:59:37 +02:00
BACKPORT: FROMLIST: dm-verity: reduce scope of real and wanted digests
In preparation for supporting multibuffer hashing where dm-verity will need to keep track of the real and wanted digests for multiple data blocks simultaneously, stop using the want_digest and real_digest fields of struct dm_verity_io from so many different places. Specifically: - Make various functions take want_digest as a parameter rather than having it be implicitly passed via the struct dm_verity_io. - Add a new tmp_digest field, and use this instead of real_digest when computing a hash solely for the purpose of immediately checking it. The result is that real_digest and want_digest are used only by verity_verify_io(). Reviewed-by: Sami Tolvanen <samitolvanen@google.com> Acked-by: Ard Biesheuvel <ardb@kernel.org> Signed-off-by: Eric Biggers <ebiggers@google.com> Bug: 330611177 Link: https://lore.kernel.org/r/20240621165922.77672-15-ebiggers@kernel.org (resolved conflict due to missing the upstream commit "dm-verity: Convert from tasklet to BH workqueue", which is hard to cherry-pick because it depends on workqueue subsystem changes) Change-Id: I1d417ed3f79d9bcf11c09128e653e695dc25081e Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
parent
3503ed6feb
commit
6c33cbb433
|
@ -187,11 +187,10 @@ static int fec_is_erasure(struct dm_verity *v, struct dm_verity_io *io,
|
|||
u8 *want_digest, u8 *data)
|
||||
{
|
||||
if (unlikely(verity_hash(v, io, data, 1 << v->data_dev_block_bits,
|
||||
verity_io_real_digest(v, io), true)))
|
||||
io->tmp_digest, true)))
|
||||
return 0;
|
||||
|
||||
return memcmp(verity_io_real_digest(v, io), want_digest,
|
||||
v->digest_size) != 0;
|
||||
return memcmp(io->tmp_digest, want_digest, v->digest_size) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -362,7 +361,7 @@ static void fec_init_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
|
|||
*/
|
||||
static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io,
|
||||
struct dm_verity_fec_io *fio, u64 rsb, u64 offset,
|
||||
bool use_erasures)
|
||||
const u8 *want_digest, bool use_erasures)
|
||||
{
|
||||
int r, neras = 0;
|
||||
unsigned int pos;
|
||||
|
@ -388,12 +387,11 @@ static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io,
|
|||
|
||||
/* Always re-validate the corrected block against the expected hash */
|
||||
r = verity_hash(v, io, fio->output, 1 << v->data_dev_block_bits,
|
||||
verity_io_real_digest(v, io), true);
|
||||
io->tmp_digest, true);
|
||||
if (unlikely(r < 0))
|
||||
return r;
|
||||
|
||||
if (memcmp(verity_io_real_digest(v, io), verity_io_want_digest(v, io),
|
||||
v->digest_size)) {
|
||||
if (memcmp(io->tmp_digest, want_digest, v->digest_size)) {
|
||||
DMERR_LIMIT("%s: FEC %llu: failed to correct (%d erasures)",
|
||||
v->data_dev->name, (unsigned long long)rsb, neras);
|
||||
return -EILSEQ;
|
||||
|
@ -404,7 +402,8 @@ static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io,
|
|||
|
||||
/* Correct errors in a block. Copies corrected block to dest. */
|
||||
int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
|
||||
enum verity_block_type type, sector_t block, u8 *dest)
|
||||
enum verity_block_type type, const u8 *want_digest,
|
||||
sector_t block, u8 *dest)
|
||||
{
|
||||
int r;
|
||||
struct dm_verity_fec_io *fio = fec_io(io);
|
||||
|
@ -447,9 +446,9 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
|
|||
* them first. Do a second attempt with erasures if the corruption is
|
||||
* bad enough.
|
||||
*/
|
||||
r = fec_decode_rsb(v, io, fio, rsb, offset, false);
|
||||
r = fec_decode_rsb(v, io, fio, rsb, offset, want_digest, false);
|
||||
if (r < 0) {
|
||||
r = fec_decode_rsb(v, io, fio, rsb, offset, true);
|
||||
r = fec_decode_rsb(v, io, fio, rsb, offset, want_digest, true);
|
||||
if (r < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,8 @@ struct dm_verity_fec_io {
|
|||
extern bool verity_fec_is_enabled(struct dm_verity *v);
|
||||
|
||||
extern int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
|
||||
enum verity_block_type type, sector_t block,
|
||||
u8 *dest);
|
||||
enum verity_block_type type, const u8 *want_digest,
|
||||
sector_t block, u8 *dest);
|
||||
|
||||
extern unsigned int verity_fec_status_table(struct dm_verity *v, unsigned int sz,
|
||||
char *result, unsigned int maxlen);
|
||||
|
@ -99,6 +99,7 @@ static inline bool verity_fec_is_enabled(struct dm_verity *v)
|
|||
static inline int verity_fec_decode(struct dm_verity *v,
|
||||
struct dm_verity_io *io,
|
||||
enum verity_block_type type,
|
||||
const u8 *want_digest,
|
||||
sector_t block, u8 *dest)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
@ -285,12 +285,12 @@ out:
|
|||
* Verify hash of a metadata block pertaining to the specified data block
|
||||
* ("block" argument) at a specified level ("level" argument).
|
||||
*
|
||||
* On successful return, verity_io_want_digest(v, io) contains the hash value
|
||||
* for a lower tree level or for the data block (if we're at the lowest level).
|
||||
* On successful return, want_digest contains the hash value for a lower tree
|
||||
* level or for the data block (if we're at the lowest level).
|
||||
*
|
||||
* If "skip_unverified" is true, unverified buffer is skipped and 1 is returned.
|
||||
* If "skip_unverified" is false, unverified buffer is hashed and verified
|
||||
* against current value of verity_io_want_digest(v, io).
|
||||
* against current value of want_digest.
|
||||
*/
|
||||
static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
|
||||
sector_t block, int level, bool skip_unverified,
|
||||
|
@ -333,11 +333,11 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
|
|||
}
|
||||
|
||||
r = verity_hash(v, io, data, 1 << v->hash_dev_block_bits,
|
||||
verity_io_real_digest(v, io), !io->in_tasklet);
|
||||
io->tmp_digest, !io->in_tasklet);
|
||||
if (unlikely(r < 0))
|
||||
goto release_ret_r;
|
||||
|
||||
if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
|
||||
if (likely(memcmp(io->tmp_digest, want_digest,
|
||||
v->digest_size) == 0))
|
||||
aux->hash_verified = 1;
|
||||
else if (static_branch_unlikely(&use_tasklet_enabled) &&
|
||||
|
@ -349,7 +349,7 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
|
|||
r = -EAGAIN;
|
||||
goto release_ret_r;
|
||||
} else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_METADATA,
|
||||
hash_block, data) == 0)
|
||||
want_digest, hash_block, data) == 0)
|
||||
aux->hash_verified = 1;
|
||||
else if (verity_handle_err(v,
|
||||
DM_VERITY_BLOCK_TYPE_METADATA,
|
||||
|
@ -412,7 +412,8 @@ out:
|
|||
}
|
||||
|
||||
static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io,
|
||||
sector_t cur_block, u8 *dest)
|
||||
const u8 *want_digest, sector_t cur_block,
|
||||
u8 *dest)
|
||||
{
|
||||
struct page *page;
|
||||
void *buffer;
|
||||
|
@ -436,12 +437,11 @@ static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io,
|
|||
goto free_ret;
|
||||
|
||||
r = verity_hash(v, io, buffer, 1 << v->data_dev_block_bits,
|
||||
verity_io_real_digest(v, io), true);
|
||||
io->tmp_digest, true);
|
||||
if (unlikely(r))
|
||||
goto free_ret;
|
||||
|
||||
if (memcmp(verity_io_real_digest(v, io),
|
||||
verity_io_want_digest(v, io), v->digest_size)) {
|
||||
if (memcmp(io->tmp_digest, want_digest, v->digest_size)) {
|
||||
r = -EIO;
|
||||
goto free_ret;
|
||||
}
|
||||
|
@ -456,8 +456,9 @@ free_ret:
|
|||
|
||||
static int verity_handle_data_hash_mismatch(struct dm_verity *v,
|
||||
struct dm_verity_io *io,
|
||||
struct bio *bio, sector_t blkno,
|
||||
u8 *data)
|
||||
struct bio *bio,
|
||||
const u8 *want_digest,
|
||||
sector_t blkno, u8 *data)
|
||||
{
|
||||
if (static_branch_unlikely(&use_tasklet_enabled) && io->in_tasklet) {
|
||||
/*
|
||||
|
@ -466,14 +467,14 @@ static int verity_handle_data_hash_mismatch(struct dm_verity *v,
|
|||
*/
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (verity_recheck(v, io, blkno, data) == 0) {
|
||||
if (verity_recheck(v, io, want_digest, blkno, data) == 0) {
|
||||
if (v->validated_blocks)
|
||||
set_bit(blkno, v->validated_blocks);
|
||||
return 0;
|
||||
}
|
||||
#if defined(CONFIG_DM_VERITY_FEC)
|
||||
if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA, blkno,
|
||||
data) == 0)
|
||||
if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA, want_digest,
|
||||
blkno, data) == 0)
|
||||
return 0;
|
||||
#endif
|
||||
if (bio->bi_status)
|
||||
|
@ -564,8 +565,9 @@ static int verity_verify_io(struct dm_verity_io *io)
|
|||
kunmap_local(data);
|
||||
continue;
|
||||
}
|
||||
r = verity_handle_data_hash_mismatch(v, io, bio, cur_block,
|
||||
data);
|
||||
r = verity_handle_data_hash_mismatch(v, io, bio,
|
||||
verity_io_want_digest(v, io),
|
||||
cur_block, data);
|
||||
kunmap_local(data);
|
||||
if (unlikely(r))
|
||||
return r;
|
||||
|
|
|
@ -90,6 +90,7 @@ struct dm_verity_io {
|
|||
|
||||
struct work_struct work;
|
||||
|
||||
u8 tmp_digest[HASH_MAX_DIGESTSIZE];
|
||||
u8 real_digest[HASH_MAX_DIGESTSIZE];
|
||||
u8 want_digest[HASH_MAX_DIGESTSIZE];
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user