Loading Documentation/device-mapper/verity.txt +11 −1 Original line number Original line Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 =================== =================== Loading drivers/md/dm-verity.c +89 −9 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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 */ Loading Loading @@ -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). Loading Loading @@ -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; } } Loading Loading @@ -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; } } } } Loading Loading @@ -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; } } Loading Loading @@ -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); Loading Loading
Documentation/device-mapper/verity.txt +11 −1 Original line number Original line Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 =================== =================== Loading
drivers/md/dm-verity.c +89 −9 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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 */ Loading Loading @@ -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). Loading Loading @@ -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; } } Loading Loading @@ -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; } } } } Loading Loading @@ -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; } } Loading Loading @@ -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); Loading