Skip to content

Commit 843f38d

Browse files
Patrik Torstenssonsnitm
authored andcommitted
dm verity: add 'check_at_most_once' option to only validate hashes once
This allows platforms that are CPU/memory contrained to verify data blocks only the first time they are read from the data device, rather than every time. As such, it provides a reduced level of security because only offline tampering of the data device's content will be detected, not online tampering. Hash blocks are still verified each time they are read from the hash device, since verification of hash blocks is less performance critical than data blocks, and a hash block will not be verified any more after all the data blocks it covers have been verified anyway. This option introduces a bitset that is used to check if a block has been validated before or not. A block can be validated more than once as there is no thread protection for the bitset. These changes were developed and tested on entry-level Android Go devices. Signed-off-by: Patrik Torstensson <[email protected]> Signed-off-by: Mike Snitzer <[email protected]>
1 parent 45354f1 commit 843f38d

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed

Documentation/device-mapper/verity.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@ fec_start <offset>
109109
This is the offset, in <data_block_size> blocks, from the start of the
110110
FEC device to the beginning of the encoding data.
111111

112+
check_at_most_once
113+
Verify data blocks only the first time they are read from the data device,
114+
rather than every time. This reduces the overhead of dm-verity so that it
115+
can be used on systems that are memory and/or CPU constrained. However, it
116+
provides a reduced level of security because only offline tampering of the
117+
data device's content will be detected, not online tampering.
118+
119+
Hash blocks are still verified each time they are read from the hash device,
120+
since verification of hash blocks is less performance critical than data
121+
blocks, and a hash block will not be verified any more after all the data
122+
blocks it covers have been verified anyway.
112123

113124
Theory of operation
114125
===================

drivers/md/dm-verity-target.c

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
3333
#define DM_VERITY_OPT_RESTART "restart_on_corruption"
3434
#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
35+
#define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
3536

3637
#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
3738

@@ -432,6 +433,18 @@ static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
432433
return 0;
433434
}
434435

436+
/*
437+
* Moves the bio iter one data block forward.
438+
*/
439+
static inline void verity_bv_skip_block(struct dm_verity *v,
440+
struct dm_verity_io *io,
441+
struct bvec_iter *iter)
442+
{
443+
struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
444+
445+
bio_advance_iter(bio, iter, 1 << v->data_dev_block_bits);
446+
}
447+
435448
/*
436449
* Verify one "dm_verity_io" structure.
437450
*/
@@ -445,9 +458,16 @@ static int verity_verify_io(struct dm_verity_io *io)
445458

446459
for (b = 0; b < io->n_blocks; b++) {
447460
int r;
461+
sector_t cur_block = io->block + b;
448462
struct ahash_request *req = verity_io_hash_req(v, io);
449463

450-
r = verity_hash_for_block(v, io, io->block + b,
464+
if (v->validated_blocks &&
465+
likely(test_bit(cur_block, v->validated_blocks))) {
466+
verity_bv_skip_block(v, io, &io->iter);
467+
continue;
468+
}
469+
470+
r = verity_hash_for_block(v, io, cur_block,
451471
verity_io_want_digest(v, io),
452472
&is_zero);
453473
if (unlikely(r < 0))
@@ -481,13 +501,16 @@ static int verity_verify_io(struct dm_verity_io *io)
481501
return r;
482502

483503
if (likely(memcmp(verity_io_real_digest(v, io),
484-
verity_io_want_digest(v, io), v->digest_size) == 0))
504+
verity_io_want_digest(v, io), v->digest_size) == 0)) {
505+
if (v->validated_blocks)
506+
set_bit(cur_block, v->validated_blocks);
485507
continue;
508+
}
486509
else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
487-
io->block + b, NULL, &start) == 0)
510+
cur_block, NULL, &start) == 0)
488511
continue;
489512
else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
490-
io->block + b))
513+
cur_block))
491514
return -EIO;
492515
}
493516

@@ -673,6 +696,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
673696
args += DM_VERITY_OPTS_FEC;
674697
if (v->zero_digest)
675698
args++;
699+
if (v->validated_blocks)
700+
args++;
676701
if (!args)
677702
return;
678703
DMEMIT(" %u", args);
@@ -691,6 +716,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
691716
}
692717
if (v->zero_digest)
693718
DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
719+
if (v->validated_blocks)
720+
DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE);
694721
sz = verity_fec_status_table(v, sz, result, maxlen);
695722
break;
696723
}
@@ -740,6 +767,7 @@ static void verity_dtr(struct dm_target *ti)
740767
if (v->bufio)
741768
dm_bufio_client_destroy(v->bufio);
742769

770+
kvfree(v->validated_blocks);
743771
kfree(v->salt);
744772
kfree(v->root_digest);
745773
kfree(v->zero_digest);
@@ -760,6 +788,26 @@ static void verity_dtr(struct dm_target *ti)
760788
kfree(v);
761789
}
762790

791+
static int verity_alloc_most_once(struct dm_verity *v)
792+
{
793+
struct dm_target *ti = v->ti;
794+
795+
/* the bitset can only handle INT_MAX blocks */
796+
if (v->data_blocks > INT_MAX) {
797+
ti->error = "device too large to use check_at_most_once";
798+
return -E2BIG;
799+
}
800+
801+
v->validated_blocks = kvzalloc(BITS_TO_LONGS(v->data_blocks) *
802+
sizeof(unsigned long), GFP_KERNEL);
803+
if (!v->validated_blocks) {
804+
ti->error = "failed to allocate bitset for check_at_most_once";
805+
return -ENOMEM;
806+
}
807+
808+
return 0;
809+
}
810+
763811
static int verity_alloc_zero_digest(struct dm_verity *v)
764812
{
765813
int r = -ENOMEM;
@@ -829,6 +877,12 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
829877
}
830878
continue;
831879

880+
} else if (!strcasecmp(arg_name, DM_VERITY_OPT_AT_MOST_ONCE)) {
881+
r = verity_alloc_most_once(v);
882+
if (r)
883+
return r;
884+
continue;
885+
832886
} else if (verity_is_fec_opt_arg(arg_name)) {
833887
r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
834888
if (r)
@@ -1096,7 +1150,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
10961150

10971151
static struct target_type verity_target = {
10981152
.name = "verity",
1099-
.version = {1, 3, 0},
1153+
.version = {1, 4, 0},
11001154
.module = THIS_MODULE,
11011155
.ctr = verity_ctr,
11021156
.dtr = verity_dtr,

drivers/md/dm-verity.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct dm_verity {
6363
sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
6464

6565
struct dm_verity_fec *fec; /* forward error correction */
66+
unsigned long *validated_blocks; /* bitset blocks validated */
6667
};
6768

6869
struct dm_verity_io {

0 commit comments

Comments
 (0)