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

Configure Feed

Select the types of activity you want to include in your feed.

dm-verity: recheck the hash after a failure

If a userspace process reads (with O_DIRECT) multiple blocks into the same
buffer, dm-verity reports an error [1].

This commit fixes dm-verity, so that if hash verification fails, the data
is read again into a kernel buffer (where userspace can't modify it) and
the hash is rechecked. If the recheck succeeds, the content of the kernel
buffer is copied into the user buffer; if the recheck fails, an error is
reported.

[1] https://people.redhat.com/~mpatocka/testcases/blk-auth-modify/read2.c

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Mike Snitzer <snitzer@kernel.org>

authored by

Mikulas Patocka and committed by
Mike Snitzer
9177f3c0 c88f5e55

+86 -6
+80 -6
drivers/md/dm-verity-target.c
··· 482 482 return 0; 483 483 } 484 484 485 + static int verity_recheck_copy(struct dm_verity *v, struct dm_verity_io *io, 486 + u8 *data, size_t len) 487 + { 488 + memcpy(data, io->recheck_buffer, len); 489 + io->recheck_buffer += len; 490 + 491 + return 0; 492 + } 493 + 494 + static int verity_recheck(struct dm_verity *v, struct dm_verity_io *io, 495 + struct bvec_iter start, sector_t cur_block) 496 + { 497 + struct page *page; 498 + void *buffer; 499 + int r; 500 + struct dm_io_request io_req; 501 + struct dm_io_region io_loc; 502 + 503 + page = mempool_alloc(&v->recheck_pool, GFP_NOIO); 504 + buffer = page_to_virt(page); 505 + 506 + io_req.bi_opf = REQ_OP_READ; 507 + io_req.mem.type = DM_IO_KMEM; 508 + io_req.mem.ptr.addr = buffer; 509 + io_req.notify.fn = NULL; 510 + io_req.client = v->io; 511 + io_loc.bdev = v->data_dev->bdev; 512 + io_loc.sector = cur_block << (v->data_dev_block_bits - SECTOR_SHIFT); 513 + io_loc.count = 1 << (v->data_dev_block_bits - SECTOR_SHIFT); 514 + r = dm_io(&io_req, 1, &io_loc, NULL); 515 + if (unlikely(r)) 516 + goto free_ret; 517 + 518 + r = verity_hash(v, verity_io_hash_req(v, io), buffer, 519 + 1 << v->data_dev_block_bits, 520 + verity_io_real_digest(v, io), true); 521 + if (unlikely(r)) 522 + goto free_ret; 523 + 524 + if (memcmp(verity_io_real_digest(v, io), 525 + verity_io_want_digest(v, io), v->digest_size)) { 526 + r = -EIO; 527 + goto free_ret; 528 + } 529 + 530 + io->recheck_buffer = buffer; 531 + r = verity_for_bv_block(v, io, &start, verity_recheck_copy); 532 + if (unlikely(r)) 533 + goto free_ret; 534 + 535 + r = 0; 536 + free_ret: 537 + mempool_free(page, &v->recheck_pool); 538 + 539 + return r; 540 + } 541 + 485 542 static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io, 486 543 u8 *data, size_t len) 487 544 { ··· 565 508 { 566 509 bool is_zero; 567 510 struct dm_verity *v = io->v; 568 - #if defined(CONFIG_DM_VERITY_FEC) 569 511 struct bvec_iter start; 570 - #endif 571 512 struct bvec_iter iter_copy; 572 513 struct bvec_iter *iter; 573 514 struct crypto_wait wait; ··· 616 561 if (unlikely(r < 0)) 617 562 return r; 618 563 619 - #if defined(CONFIG_DM_VERITY_FEC) 620 - if (verity_fec_is_enabled(v)) 621 - start = *iter; 622 - #endif 564 + start = *iter; 623 565 r = verity_for_io_block(v, io, iter, &wait); 624 566 if (unlikely(r < 0)) 625 567 return r; ··· 638 586 * tasklet since it may sleep, so fallback to work-queue. 639 587 */ 640 588 return -EAGAIN; 589 + } else if (verity_recheck(v, io, start, cur_block) == 0) { 590 + if (v->validated_blocks) 591 + set_bit(cur_block, v->validated_blocks); 592 + continue; 641 593 #if defined(CONFIG_DM_VERITY_FEC) 642 594 } else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA, 643 595 cur_block, NULL, &start) == 0) { ··· 996 940 997 941 if (v->verify_wq) 998 942 destroy_workqueue(v->verify_wq); 943 + 944 + mempool_exit(&v->recheck_pool); 945 + if (v->io) 946 + dm_io_client_destroy(v->io); 999 947 1000 948 if (v->bufio) 1001 949 dm_bufio_client_destroy(v->bufio); ··· 1438 1378 hash_position += s; 1439 1379 } 1440 1380 v->hash_blocks = hash_position; 1381 + 1382 + r = mempool_init_page_pool(&v->recheck_pool, 1, 0); 1383 + if (unlikely(r)) { 1384 + ti->error = "Cannot allocate mempool"; 1385 + goto bad; 1386 + } 1387 + 1388 + v->io = dm_io_client_create(); 1389 + if (IS_ERR(v->io)) { 1390 + r = PTR_ERR(v->io); 1391 + v->io = NULL; 1392 + ti->error = "Cannot allocate dm io"; 1393 + goto bad; 1394 + } 1441 1395 1442 1396 v->bufio = dm_bufio_client_create(v->hash_dev->bdev, 1443 1397 1 << v->hash_dev_block_bits, 1, sizeof(struct buffer_aux),
+6
drivers/md/dm-verity.h
··· 11 11 #ifndef DM_VERITY_H 12 12 #define DM_VERITY_H 13 13 14 + #include <linux/dm-io.h> 14 15 #include <linux/dm-bufio.h> 15 16 #include <linux/device-mapper.h> 16 17 #include <linux/interrupt.h> ··· 69 68 unsigned long *validated_blocks; /* bitset blocks validated */ 70 69 71 70 char *signature_key_desc; /* signature keyring reference */ 71 + 72 + struct dm_io_client *io; 73 + mempool_t recheck_pool; 72 74 }; 73 75 74 76 struct dm_verity_io { ··· 87 83 struct bvec_iter iter; 88 84 89 85 struct work_struct work; 86 + 87 + char *recheck_buffer; 90 88 91 89 /* 92 90 * Three variably-size fields follow this struct: