summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrik Torstensson <totte@google.com>2018-03-22 21:18:04 -0400
committerMike Snitzer <snitzer@redhat.com>2018-04-03 15:04:29 -0400
commit843f38d382b1ca2f6f4ae2ef7c35933e6319ffbb (patch)
treea056d2fe94b2bb94ee075b8d1ae6f03d87b81382
parent45354f1eb67224669a1de94dbfcb8bb226be1f74 (diff)
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 <totte@google.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--Documentation/device-mapper/verity.txt11
-rw-r--r--drivers/md/dm-verity-target.c64
-rw-r--r--drivers/md/dm-verity.h1
3 files changed, 71 insertions, 5 deletions
diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt
index 89fd8f9a259f..b3d2e4a42255 100644
--- a/Documentation/device-mapper/verity.txt
+++ b/Documentation/device-mapper/verity.txt
@@ -109,6 +109,17 @@ fec_start <offset>
109 This is the offset, in <data_block_size> blocks, from the start of the 109 This is the offset, in <data_block_size> blocks, from the start of the
110 FEC device to the beginning of the encoding data. 110 FEC device to the beginning of the encoding data.
111 111
112check_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.
112 123
113Theory of operation 124Theory of operation
114=================== 125===================
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index aedb8222836b..14c620992794 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -32,6 +32,7 @@
32#define DM_VERITY_OPT_LOGGING "ignore_corruption" 32#define DM_VERITY_OPT_LOGGING "ignore_corruption"
33#define DM_VERITY_OPT_RESTART "restart_on_corruption" 33#define DM_VERITY_OPT_RESTART "restart_on_corruption"
34#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks" 34#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
35#define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
35 36
36#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC) 37#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
37 38
@@ -433,6 +434,18 @@ static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
433} 434}
434 435
435/* 436/*
437 * Moves the bio iter one data block forward.
438 */
439static 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
448/*
436 * Verify one "dm_verity_io" structure. 449 * Verify one "dm_verity_io" structure.
437 */ 450 */
438static int verity_verify_io(struct dm_verity_io *io) 451static int verity_verify_io(struct dm_verity_io *io)
@@ -445,9 +458,16 @@ static int verity_verify_io(struct dm_verity_io *io)
445 458
446 for (b = 0; b < io->n_blocks; b++) { 459 for (b = 0; b < io->n_blocks; b++) {
447 int r; 460 int r;
461 sector_t cur_block = io->block + b;
448 struct ahash_request *req = verity_io_hash_req(v, io); 462 struct ahash_request *req = verity_io_hash_req(v, io);
449 463
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,
451 verity_io_want_digest(v, io), 471 verity_io_want_digest(v, io),
452 &is_zero); 472 &is_zero);
453 if (unlikely(r < 0)) 473 if (unlikely(r < 0))
@@ -481,13 +501,16 @@ static int verity_verify_io(struct dm_verity_io *io)
481 return r; 501 return r;
482 502
483 if (likely(memcmp(verity_io_real_digest(v, io), 503 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);
485 continue; 507 continue;
508 }
486 else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA, 509 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)
488 continue; 511 continue;
489 else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA, 512 else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
490 io->block + b)) 513 cur_block))
491 return -EIO; 514 return -EIO;
492 } 515 }
493 516
@@ -673,6 +696,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
673 args += DM_VERITY_OPTS_FEC; 696 args += DM_VERITY_OPTS_FEC;
674 if (v->zero_digest) 697 if (v->zero_digest)
675 args++; 698 args++;
699 if (v->validated_blocks)
700 args++;
676 if (!args) 701 if (!args)
677 return; 702 return;
678 DMEMIT(" %u", args); 703 DMEMIT(" %u", args);
@@ -691,6 +716,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
691 } 716 }
692 if (v->zero_digest) 717 if (v->zero_digest)
693 DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES); 718 DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
719 if (v->validated_blocks)
720 DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE);
694 sz = verity_fec_status_table(v, sz, result, maxlen); 721 sz = verity_fec_status_table(v, sz, result, maxlen);
695 break; 722 break;
696 } 723 }
@@ -740,6 +767,7 @@ static void verity_dtr(struct dm_target *ti)
740 if (v->bufio) 767 if (v->bufio)
741 dm_bufio_client_destroy(v->bufio); 768 dm_bufio_client_destroy(v->bufio);
742 769
770 kvfree(v->validated_blocks);
743 kfree(v->salt); 771 kfree(v->salt);
744 kfree(v->root_digest); 772 kfree(v->root_digest);
745 kfree(v->zero_digest); 773 kfree(v->zero_digest);
@@ -760,6 +788,26 @@ static void verity_dtr(struct dm_target *ti)
760 kfree(v); 788 kfree(v);
761} 789}
762 790
791static 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
763static int verity_alloc_zero_digest(struct dm_verity *v) 811static int verity_alloc_zero_digest(struct dm_verity *v)
764{ 812{
765 int r = -ENOMEM; 813 int r = -ENOMEM;
@@ -829,6 +877,12 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
829 } 877 }
830 continue; 878 continue;
831 879
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
832 } else if (verity_is_fec_opt_arg(arg_name)) { 886 } else if (verity_is_fec_opt_arg(arg_name)) {
833 r = verity_fec_parse_opt_args(as, v, &argc, arg_name); 887 r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
834 if (r) 888 if (r)
@@ -1096,7 +1150,7 @@ bad:
1096 1150
1097static struct target_type verity_target = { 1151static struct target_type verity_target = {
1098 .name = "verity", 1152 .name = "verity",
1099 .version = {1, 3, 0}, 1153 .version = {1, 4, 0},
1100 .module = THIS_MODULE, 1154 .module = THIS_MODULE,
1101 .ctr = verity_ctr, 1155 .ctr = verity_ctr,
1102 .dtr = verity_dtr, 1156 .dtr = verity_dtr,
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
index 69f9a298f8c9..3441c10b840c 100644
--- a/drivers/md/dm-verity.h
+++ b/drivers/md/dm-verity.h
@@ -63,6 +63,7 @@ struct dm_verity {
63 sector_t hash_level_block[DM_VERITY_MAX_LEVELS]; 63 sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
64 64
65 struct dm_verity_fec *fec; /* forward error correction */ 65 struct dm_verity_fec *fec; /* forward error correction */
66 unsigned long *validated_blocks; /* bitset blocks validated */
66}; 67};
67 68
68struct dm_verity_io { 69struct dm_verity_io {