Skip to content

Commit 65ff5b7

Browse files
samitolvanensnitm
authored andcommitted
dm verity: add error handling modes for corrupted blocks
Add device specific modes to dm-verity to specify how corrupted blocks should be handled. The following modes are defined: - DM_VERITY_MODE_EIO is the default behavior, where reading a corrupted block results in -EIO. - DM_VERITY_MODE_LOGGING only logs corrupted blocks, but does not block the read. - DM_VERITY_MODE_RESTART calls kernel_restart when a corrupted block is discovered. In addition, each mode sends a uevent to notify userspace of corruption and to allow further recovery actions. The driver defaults to previous behavior (DM_VERITY_MODE_EIO) and other modes can be enabled with an additional parameter to the verity table. Signed-off-by: Sami Tolvanen <[email protected]> Signed-off-by: Mike Snitzer <[email protected]>
1 parent 0e0e32c commit 65ff5b7

File tree

3 files changed

+153
-12
lines changed

3 files changed

+153
-12
lines changed

Documentation/device-mapper/verity.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Construction Parameters
1111
<data_block_size> <hash_block_size>
1212
<num_data_blocks> <hash_start_block>
1313
<algorithm> <digest> <salt>
14+
[<#opt_params> <opt_params>]
1415

1516
<version>
1617
This is the type of the on-disk hash format.
@@ -62,6 +63,22 @@ Construction Parameters
6263
<salt>
6364
The hexadecimal encoding of the salt value.
6465

66+
<#opt_params>
67+
Number of optional parameters. If there are no optional parameters,
68+
the optional paramaters section can be skipped or #opt_params can be zero.
69+
Otherwise #opt_params is the number of following arguments.
70+
71+
Example of optional parameters section:
72+
1 ignore_corruption
73+
74+
ignore_corruption
75+
Log corrupted blocks, but allow read operations to proceed normally.
76+
77+
restart_on_corruption
78+
Restart the system when a corrupted block is discovered. This option is
79+
not compatible with ignore_corruption and requires user space support to
80+
avoid restart loops.
81+
6582
Theory of operation
6683
===================
6784

drivers/md/dm-verity.c

Lines changed: 135 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,39 @@
1818

1919
#include <linux/module.h>
2020
#include <linux/device-mapper.h>
21+
#include <linux/reboot.h>
2122
#include <crypto/hash.h>
2223

2324
#define DM_MSG_PREFIX "verity"
2425

26+
#define DM_VERITY_ENV_LENGTH 42
27+
#define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR"
28+
2529
#define DM_VERITY_IO_VEC_INLINE 16
2630
#define DM_VERITY_MEMPOOL_SIZE 4
2731
#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
2832

2933
#define DM_VERITY_MAX_LEVELS 63
34+
#define DM_VERITY_MAX_CORRUPTED_ERRS 100
35+
36+
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
37+
#define DM_VERITY_OPT_RESTART "restart_on_corruption"
3038

3139
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
3240

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

43+
enum verity_mode {
44+
DM_VERITY_MODE_EIO,
45+
DM_VERITY_MODE_LOGGING,
46+
DM_VERITY_MODE_RESTART
47+
};
48+
49+
enum verity_block_type {
50+
DM_VERITY_BLOCK_TYPE_DATA,
51+
DM_VERITY_BLOCK_TYPE_METADATA
52+
};
53+
3554
struct dm_verity {
3655
struct dm_dev *data_dev;
3756
struct dm_dev *hash_dev;
@@ -54,6 +73,8 @@ struct dm_verity {
5473
unsigned digest_size; /* digest size for the current hash algorithm */
5574
unsigned shash_descsize;/* the size of temporary space for crypto */
5675
int hash_failed; /* set to 1 if hash of any block failed */
76+
enum verity_mode mode; /* mode for handling verification errors */
77+
unsigned corrupted_errs;/* Number of errors for corrupted blocks */
5778

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

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

198+
/*
199+
* Handle verification errors.
200+
*/
201+
static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
202+
unsigned long long block)
203+
{
204+
char verity_env[DM_VERITY_ENV_LENGTH];
205+
char *envp[] = { verity_env, NULL };
206+
const char *type_str = "";
207+
struct mapped_device *md = dm_table_get_md(v->ti->table);
208+
209+
/* Corruption should be visible in device status in all modes */
210+
v->hash_failed = 1;
211+
212+
if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
213+
goto out;
214+
215+
v->corrupted_errs++;
216+
217+
switch (type) {
218+
case DM_VERITY_BLOCK_TYPE_DATA:
219+
type_str = "data";
220+
break;
221+
case DM_VERITY_BLOCK_TYPE_METADATA:
222+
type_str = "metadata";
223+
break;
224+
default:
225+
BUG();
226+
}
227+
228+
DMERR("%s: %s block %llu is corrupted", v->data_dev->name, type_str,
229+
block);
230+
231+
if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS)
232+
DMERR("%s: reached maximum errors", v->data_dev->name);
233+
234+
snprintf(verity_env, DM_VERITY_ENV_LENGTH, "%s=%d,%llu",
235+
DM_VERITY_ENV_VAR_NAME, type, block);
236+
237+
kobject_uevent_env(&disk_to_dev(dm_disk(md))->kobj, KOBJ_CHANGE, envp);
238+
239+
out:
240+
if (v->mode == DM_VERITY_MODE_LOGGING)
241+
return 0;
242+
243+
if (v->mode == DM_VERITY_MODE_RESTART)
244+
kernel_restart("dm-verity device corrupted");
245+
246+
return 1;
247+
}
248+
177249
/*
178250
* Verify hash of a metadata block pertaining to the specified data block
179251
* ("block" argument) at a specified level ("level" argument).
@@ -251,11 +323,11 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
251323
goto release_ret_r;
252324
}
253325
if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
254-
DMERR_LIMIT("metadata block %llu is corrupted",
255-
(unsigned long long)hash_block);
256-
v->hash_failed = 1;
257-
r = -EIO;
258-
goto release_ret_r;
326+
if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
327+
hash_block)) {
328+
r = -EIO;
329+
goto release_ret_r;
330+
}
259331
} else
260332
aux->hash_verified = 1;
261333
}
@@ -367,10 +439,9 @@ static int verity_verify_io(struct dm_verity_io *io)
367439
return r;
368440
}
369441
if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
370-
DMERR_LIMIT("data block %llu is corrupted",
371-
(unsigned long long)(io->block + b));
372-
v->hash_failed = 1;
373-
return -EIO;
442+
if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
443+
io->block + b))
444+
return -EIO;
374445
}
375446
}
376447

@@ -546,6 +617,19 @@ static void verity_status(struct dm_target *ti, status_type_t type,
546617
else
547618
for (x = 0; x < v->salt_size; x++)
548619
DMEMIT("%02x", v->salt[x]);
620+
if (v->mode != DM_VERITY_MODE_EIO) {
621+
DMEMIT(" 1 ");
622+
switch (v->mode) {
623+
case DM_VERITY_MODE_LOGGING:
624+
DMEMIT(DM_VERITY_OPT_LOGGING);
625+
break;
626+
case DM_VERITY_MODE_RESTART:
627+
DMEMIT(DM_VERITY_OPT_RESTART);
628+
break;
629+
default:
630+
BUG();
631+
}
632+
}
549633
break;
550634
}
551635
}
@@ -647,13 +731,19 @@ static void verity_dtr(struct dm_target *ti)
647731
static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
648732
{
649733
struct dm_verity *v;
650-
unsigned num;
734+
struct dm_arg_set as;
735+
const char *opt_string;
736+
unsigned int num, opt_params;
651737
unsigned long long num_ll;
652738
int r;
653739
int i;
654740
sector_t hash_position;
655741
char dummy;
656742

743+
static struct dm_arg _args[] = {
744+
{0, 1, "Invalid number of feature args"},
745+
};
746+
657747
v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
658748
if (!v) {
659749
ti->error = "Cannot allocate verity structure";
@@ -668,8 +758,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
668758
goto bad;
669759
}
670760

671-
if (argc != 10) {
672-
ti->error = "Invalid argument count: exactly 10 arguments required";
761+
if (argc < 10) {
762+
ti->error = "Not enough arguments";
673763
r = -EINVAL;
674764
goto bad;
675765
}
@@ -790,6 +880,39 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
790880
}
791881
}
792882

883+
argv += 10;
884+
argc -= 10;
885+
886+
/* Optional parameters */
887+
if (argc) {
888+
as.argc = argc;
889+
as.argv = argv;
890+
891+
r = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
892+
if (r)
893+
goto bad;
894+
895+
while (opt_params) {
896+
opt_params--;
897+
opt_string = dm_shift_arg(&as);
898+
if (!opt_string) {
899+
ti->error = "Not enough feature arguments";
900+
r = -EINVAL;
901+
goto bad;
902+
}
903+
904+
if (!strcasecmp(opt_string, DM_VERITY_OPT_LOGGING))
905+
v->mode = DM_VERITY_MODE_LOGGING;
906+
else if (!strcasecmp(opt_string, DM_VERITY_OPT_RESTART))
907+
v->mode = DM_VERITY_MODE_RESTART;
908+
else {
909+
ti->error = "Invalid feature arguments";
910+
r = -EINVAL;
911+
goto bad;
912+
}
913+
}
914+
}
915+
793916
v->hash_per_block_bits =
794917
__fls((1 << v->hash_dev_block_bits) / v->digest_size);
795918

drivers/md/dm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3483,6 +3483,7 @@ struct gendisk *dm_disk(struct mapped_device *md)
34833483
{
34843484
return md->disk;
34853485
}
3486+
EXPORT_SYMBOL_GPL(dm_disk);
34863487

34873488
struct kobject *dm_kobject(struct mapped_device *md)
34883489
{

0 commit comments

Comments
 (0)