Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dm verity: add ignore_zero_blocks feature

If ignore_zero_blocks is enabled dm-verity will return zeroes for blocks
matching a zero hash without validating the content.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>

authored by

Sami Tolvanen and committed by
Mike Snitzer
0cc37c2d a739ff3f

+93 -10
+5
Documentation/device-mapper/verity.txt
··· 79 79 not compatible with ignore_corruption and requires user space support to 80 80 avoid restart loops. 81 81 82 + ignore_zero_blocks 83 + Do not verify blocks that are expected to contain zeroes and always return 84 + zeroes instead. This may be useful if the partition contains unused blocks 85 + that are not guaranteed to contain zeroes. 86 + 82 87 use_fec_from_device <fec_dev> 83 88 Use forward error correction (FEC) to recover from corruption if hash 84 89 verification fails. Use encoding data from the specified device. This
+7 -1
drivers/md/dm-verity-fec.c
··· 205 205 u64 rsb, u64 target, unsigned block_offset, 206 206 int *neras) 207 207 { 208 + bool is_zero; 208 209 int i, j, target_index = -1; 209 210 struct dm_buffer *buf; 210 211 struct dm_bufio_client *bufio; ··· 265 264 266 265 /* locate erasures if the block is on the data device */ 267 266 if (bufio == v->fec->data_bufio && 268 - verity_hash_for_block(v, io, block, want_digest) == 0) { 267 + verity_hash_for_block(v, io, block, want_digest, 268 + &is_zero) == 0) { 269 + /* skip known zero blocks entirely */ 270 + if (is_zero) 271 + continue; 272 + 269 273 /* 270 274 * skip if we have already found the theoretical 271 275 * maximum number (i.e. fec->roots) of erasures
+79 -8
drivers/md/dm-verity-target.c
··· 31 31 32 32 #define DM_VERITY_OPT_LOGGING "ignore_corruption" 33 33 #define DM_VERITY_OPT_RESTART "restart_on_corruption" 34 + #define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks" 34 35 35 - #define DM_VERITY_OPTS_MAX (1 + DM_VERITY_OPTS_FEC) 36 + #define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC) 36 37 37 38 static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; 38 39 ··· 310 309 * of the hash tree if necessary. 311 310 */ 312 311 int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io, 313 - sector_t block, u8 *digest) 312 + sector_t block, u8 *digest, bool *is_zero) 314 313 { 315 - int i; 316 - int r; 314 + int r = 0, i; 317 315 318 316 if (likely(v->levels)) { 319 317 /* ··· 324 324 */ 325 325 r = verity_verify_level(v, io, block, 0, true, digest); 326 326 if (likely(r <= 0)) 327 - return r; 327 + goto out; 328 328 } 329 329 330 330 memcpy(digest, v->root_digest, v->digest_size); ··· 332 332 for (i = v->levels - 1; i >= 0; i--) { 333 333 r = verity_verify_level(v, io, block, i, false, digest); 334 334 if (unlikely(r)) 335 - return r; 335 + goto out; 336 336 } 337 + out: 338 + if (!r && v->zero_digest) 339 + *is_zero = !memcmp(v->zero_digest, digest, v->digest_size); 340 + else 341 + *is_zero = false; 337 342 338 - return 0; 343 + return r; 339 344 } 340 345 341 346 /* ··· 387 382 return verity_hash_update(v, verity_io_hash_desc(v, io), data, len); 388 383 } 389 384 385 + static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io, 386 + u8 *data, size_t len) 387 + { 388 + memset(data, 0, len); 389 + return 0; 390 + } 391 + 390 392 /* 391 393 * Verify one "dm_verity_io" structure. 392 394 */ 393 395 static int verity_verify_io(struct dm_verity_io *io) 394 396 { 397 + bool is_zero; 395 398 struct dm_verity *v = io->v; 396 399 struct bvec_iter start; 397 400 unsigned b; ··· 409 396 struct shash_desc *desc = verity_io_hash_desc(v, io); 410 397 411 398 r = verity_hash_for_block(v, io, io->block + b, 412 - verity_io_want_digest(v, io)); 399 + verity_io_want_digest(v, io), 400 + &is_zero); 413 401 if (unlikely(r < 0)) 414 402 return r; 403 + 404 + if (is_zero) { 405 + /* 406 + * If we expect a zero block, don't validate, just 407 + * return zeros. 408 + */ 409 + r = verity_for_bv_block(v, io, &io->iter, 410 + verity_bv_zero); 411 + if (unlikely(r < 0)) 412 + return r; 413 + 414 + continue; 415 + } 415 416 416 417 r = verity_hash_init(v, desc); 417 418 if (unlikely(r < 0)) ··· 631 604 args++; 632 605 if (verity_fec_is_enabled(v)) 633 606 args += DM_VERITY_OPTS_FEC; 607 + if (v->zero_digest) 608 + args++; 634 609 if (!args) 635 610 return; 636 611 DMEMIT(" %u", args); ··· 649 620 BUG(); 650 621 } 651 622 } 623 + if (v->zero_digest) 624 + DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES); 652 625 sz = verity_fec_status_table(v, sz, result, maxlen); 653 626 break; 654 627 } ··· 702 671 703 672 kfree(v->salt); 704 673 kfree(v->root_digest); 674 + kfree(v->zero_digest); 705 675 706 676 if (v->tfm) 707 677 crypto_free_shash(v->tfm); ··· 718 686 verity_fec_dtr(v); 719 687 720 688 kfree(v); 689 + } 690 + 691 + static int verity_alloc_zero_digest(struct dm_verity *v) 692 + { 693 + int r = -ENOMEM; 694 + struct shash_desc *desc; 695 + u8 *zero_data; 696 + 697 + v->zero_digest = kmalloc(v->digest_size, GFP_KERNEL); 698 + 699 + if (!v->zero_digest) 700 + return r; 701 + 702 + desc = kmalloc(v->shash_descsize, GFP_KERNEL); 703 + 704 + if (!desc) 705 + return r; /* verity_dtr will free zero_digest */ 706 + 707 + zero_data = kzalloc(1 << v->data_dev_block_bits, GFP_KERNEL); 708 + 709 + if (!zero_data) 710 + goto out; 711 + 712 + r = verity_hash(v, desc, zero_data, 1 << v->data_dev_block_bits, 713 + v->zero_digest); 714 + 715 + out: 716 + kfree(desc); 717 + kfree(zero_data); 718 + 719 + return r; 721 720 } 722 721 723 722 static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v) ··· 779 716 780 717 } else if (!strcasecmp(arg_name, DM_VERITY_OPT_RESTART)) { 781 718 v->mode = DM_VERITY_MODE_RESTART; 719 + continue; 720 + 721 + } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) { 722 + r = verity_alloc_zero_digest(v); 723 + if (r) { 724 + ti->error = "Cannot allocate zero digest"; 725 + return r; 726 + } 782 727 continue; 783 728 784 729 } else if (verity_is_fec_opt_arg(arg_name)) {
+2 -1
drivers/md/dm-verity.h
··· 40 40 struct crypto_shash *tfm; 41 41 u8 *root_digest; /* digest of the root block */ 42 42 u8 *salt; /* salt: its size is salt_size */ 43 + u8 *zero_digest; /* digest for a zero block */ 43 44 unsigned salt_size; 44 45 sector_t data_start; /* data offset in 512-byte sectors */ 45 46 sector_t hash_start; /* hash start in blocks */ ··· 124 123 const u8 *data, size_t len, u8 *digest); 125 124 126 125 extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io, 127 - sector_t block, u8 *digest); 126 + sector_t block, u8 *digest, bool *is_zero); 128 127 129 128 #endif /* DM_VERITY_H */