Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfd2b301 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "dm-verity: Add modes and emit uevent on corrupted blocks"

parents 81cad4e9 d48453e8
Loading
Loading
Loading
Loading
+11 −1
Original line number Original line Diff line number Diff line
@@ -10,7 +10,7 @@ Construction Parameters
    <version> <dev> <hash_dev>
    <version> <dev> <hash_dev>
    <data_block_size> <hash_block_size>
    <data_block_size> <hash_block_size>
    <num_data_blocks> <hash_start_block>
    <num_data_blocks> <hash_start_block>
    <algorithm> <digest> <salt>
    <algorithm> <digest> <salt> <mode>


<version>
<version>
    This is the type of the on-disk hash format.
    This is the type of the on-disk hash format.
@@ -62,6 +62,16 @@ Construction Parameters
<salt>
<salt>
    The hexadecimal encoding of the salt value.
    The hexadecimal encoding of the salt value.


<mode>
    Optional. The mode of operation.

    0 is the normal mode of operation where a corrupted block will result in an
      I/O error.

    1 is logging mode where corrupted blocks are logged and a uevent is sent to
      notify user space.


Theory of operation
Theory of operation
===================
===================


+89 −9
Original line number Original line Diff line number Diff line
@@ -18,20 +18,36 @@


#include <linux/module.h>
#include <linux/module.h>
#include <linux/device-mapper.h>
#include <linux/device-mapper.h>
#include <linux/reboot.h>
#include <crypto/hash.h>
#include <crypto/hash.h>


#define DM_MSG_PREFIX			"verity"
#define DM_MSG_PREFIX			"verity"


#define DM_VERITY_ENV_LENGTH		42
#define DM_VERITY_ENV_VAR_NAME		"VERITY_ERR_BLOCK_NR"

#define DM_VERITY_IO_VEC_INLINE		16
#define DM_VERITY_IO_VEC_INLINE		16
#define DM_VERITY_MEMPOOL_SIZE		4
#define DM_VERITY_MEMPOOL_SIZE		4
#define DM_VERITY_DEFAULT_PREFETCH_SIZE	262144
#define DM_VERITY_DEFAULT_PREFETCH_SIZE	262144


#define DM_VERITY_MAX_LEVELS		63
#define DM_VERITY_MAX_LEVELS		63
#define DM_VERITY_MAX_CORRUPTED_ERRS	100


static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;


module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);


enum verity_mode {
	DM_VERITY_MODE_EIO = 0,
	DM_VERITY_MODE_LOGGING = 1,
	DM_VERITY_MODE_RESTART = 2
};

enum verity_block_type {
	DM_VERITY_BLOCK_TYPE_DATA,
	DM_VERITY_BLOCK_TYPE_METADATA
};

struct dm_verity {
struct dm_verity {
	struct dm_dev *data_dev;
	struct dm_dev *data_dev;
	struct dm_dev *hash_dev;
	struct dm_dev *hash_dev;
@@ -54,6 +70,8 @@ struct dm_verity {
	unsigned digest_size;	/* digest size for the current hash algorithm */
	unsigned digest_size;	/* digest size for the current hash algorithm */
	unsigned shash_descsize;/* the size of temporary space for crypto */
	unsigned shash_descsize;/* the size of temporary space for crypto */
	int hash_failed;	/* set to 1 if hash of any block failed */
	int hash_failed;	/* set to 1 if hash of any block failed */
	enum verity_mode mode;	/* mode for handling verification errors */
	unsigned corrupted_errs;/* Number of errors for corrupted blocks */


	mempool_t *vec_mempool;	/* mempool of bio vector */
	mempool_t *vec_mempool;	/* mempool of bio vector */


@@ -174,6 +192,54 @@ static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
		*offset = idx << (v->hash_dev_block_bits - v->hash_per_block_bits);
		*offset = idx << (v->hash_dev_block_bits - v->hash_per_block_bits);
}
}


/*
 * Handle verification errors.
 */
static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
				 unsigned long long block)
{
	char verity_env[DM_VERITY_ENV_LENGTH];
	char *envp[] = { verity_env, NULL };
	const char *type_str = "";
	struct mapped_device *md = dm_table_get_md(v->ti->table);

	if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
		goto out;

	++v->corrupted_errs;

	switch (type) {
	case DM_VERITY_BLOCK_TYPE_DATA:
		type_str = "data";
		break;
	case DM_VERITY_BLOCK_TYPE_METADATA:
		type_str = "metadata";
		break;
	default:
		BUG();
	}

	DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name,
                type_str, block);

	if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS)
		DMERR("%s: reached maximum errors", v->data_dev->name);

	snprintf(verity_env, DM_VERITY_ENV_LENGTH, "%s=%d,%llu",
		DM_VERITY_ENV_VAR_NAME, type, block);

	kobject_uevent_env(&disk_to_dev(dm_disk(md))->kobj, KOBJ_CHANGE, envp);

out:
	if (v->mode == DM_VERITY_MODE_LOGGING)
		return 0;

	if (v->mode == DM_VERITY_MODE_RESTART)
		kernel_restart("dm-verity device corrupted");

	return 1;
}

/*
/*
 * Verify hash of a metadata block pertaining to the specified data block
 * Verify hash of a metadata block pertaining to the specified data block
 * ("block" argument) at a specified level ("level" argument).
 * ("block" argument) at a specified level ("level" argument).
@@ -251,11 +317,13 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
			goto release_ret_r;
			goto release_ret_r;
		}
		}
		if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
		if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
			DMERR_LIMIT("metadata block %llu is corrupted",
				(unsigned long long)hash_block);
			v->hash_failed = 1;
			v->hash_failed = 1;

			if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
					      hash_block)) {
				r = -EIO;
				r = -EIO;
				goto release_ret_r;
				goto release_ret_r;
			}
		} else
		} else
			aux->hash_verified = 1;
			aux->hash_verified = 1;
	}
	}
@@ -367,9 +435,10 @@ test_block_hash:
			return r;
			return r;
		}
		}
		if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
		if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
			DMERR_LIMIT("data block %llu is corrupted",
				(unsigned long long)(io->block + b));
			v->hash_failed = 1;
			v->hash_failed = 1;

			if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
					      io->block + b))
				return -EIO;
				return -EIO;
		}
		}
	}
	}
@@ -668,8 +737,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
		goto bad;
		goto bad;
	}
	}


	if (argc != 10) {
	if (argc < 10 || argc > 11) {
		ti->error = "Invalid argument count: exactly 10 arguments required";
		ti->error = "Invalid argument count: 10-11 arguments required";
		r = -EINVAL;
		r = -EINVAL;
		goto bad;
		goto bad;
	}
	}
@@ -790,6 +859,17 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
		}
		}
	}
	}


	if (argc > 10) {
		if (sscanf(argv[10], "%d%c", &num, &dummy) != 1 ||
			num < DM_VERITY_MODE_EIO ||
			num > DM_VERITY_MODE_RESTART) {
			ti->error = "Invalid mode";
			r = -EINVAL;
			goto bad;
		}
		v->mode = num;
	}

	v->hash_per_block_bits =
	v->hash_per_block_bits =
		__fls((1 << v->hash_dev_block_bits) / v->digest_size);
		__fls((1 << v->hash_dev_block_bits) / v->digest_size);