Commit 379475dc authored by Eric Biggers's avatar Eric Biggers Committed by Mikulas Patocka
Browse files

dm-verity: reduce scope of real and wanted digests



In preparation for supporting interleaved 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 digest solely for the purpose of immediately checking it.

The result is that real_digest and want_digest are used only by
verity_verify_io().

Signed-off-by: default avatarEric Biggers <ebiggers@kernel.org>
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
parent ba0f428c
Loading
Loading
Loading
Loading
+10 −11
Original line number Diff line number Diff line
@@ -188,14 +188,13 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_io *io,
 * Locate data block erasures using verity hashes.
 */
static int fec_is_erasure(struct dm_verity *v, struct dm_verity_io *io,
			  u8 *want_digest, u8 *data)
			  const u8 *want_digest, const u8 *data)
{
	if (unlikely(verity_hash(v, io, data, 1 << v->data_dev_block_bits,
				 verity_io_real_digest(v, io))))
				 io->tmp_digest)))
		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;
}

/*
@@ -366,7 +365,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;
@@ -392,12 +391,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));
			io->tmp_digest);
	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;
@@ -408,7 +406,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);
@@ -451,9 +450,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;
	}
+3 −2
Original line number Diff line number Diff line
@@ -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;
+20 −18
Original line number Diff line number Diff line
@@ -229,12 +229,12 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
 * 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,
@@ -273,7 +273,7 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
		if (IS_ERR(data))
			return r;
		if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_METADATA,
				      hash_block, data) == 0) {
				      want_digest, hash_block, data) == 0) {
			aux = dm_bufio_get_aux_data(buf);
			aux->hash_verified = 1;
			goto release_ok;
@@ -293,11 +293,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->tmp_digest);
		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_bh_wq_enabled) && io->in_bh) {
@@ -308,7 +308,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,
@@ -372,7 +372,8 @@ int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
}

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;
@@ -396,12 +397,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));
			io->tmp_digest);
	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;
	}
@@ -416,8 +416,9 @@ static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io,

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_bh_wq_enabled) && io->in_bh) {
		/*
@@ -426,14 +427,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)
@@ -525,8 +526,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;
+1 −0
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ struct dm_verity_io {
	struct work_struct work;
	struct work_struct bh_work;

	u8 tmp_digest[HASH_MAX_DIGESTSIZE];
	u8 real_digest[HASH_MAX_DIGESTSIZE];
	u8 want_digest[HASH_MAX_DIGESTSIZE];