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-integrity: recheck the integrity tag after a failure

If a userspace process reads (with O_DIRECT) multiple blocks into the same
buffer, dm-integrity reports an error [1]. The error is reported in a log
and it may cause RAID leg being kicked out of the array.

This commit fixes dm-integrity, so that if integrity verification fails,
the data is read again into a kernel buffer (where userspace can't modify
it) and the integrity tag is rechecked. If the recheck succeeds, the
content of the kernel buffer is copied into the user buffer; if the
recheck fails, an integrity 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
c88f5e55 54be6c6c

+84 -9
+84 -9
drivers/md/dm-integrity.c
··· 278 278 279 279 atomic64_t number_of_mismatches; 280 280 281 + mempool_t recheck_pool; 282 + 281 283 struct notifier_block reboot_notifier; 282 284 }; 283 285 ··· 1691 1689 get_random_bytes(result, ic->tag_size); 1692 1690 } 1693 1691 1692 + static void integrity_recheck(struct dm_integrity_io *dio) 1693 + { 1694 + struct bio *bio = dm_bio_from_per_bio_data(dio, sizeof(struct dm_integrity_io)); 1695 + struct dm_integrity_c *ic = dio->ic; 1696 + struct bvec_iter iter; 1697 + struct bio_vec bv; 1698 + sector_t sector, logical_sector, area, offset; 1699 + char checksum_onstack[max_t(size_t, HASH_MAX_DIGESTSIZE, MAX_TAG_SIZE)]; 1700 + struct page *page; 1701 + void *buffer; 1702 + 1703 + get_area_and_offset(ic, dio->range.logical_sector, &area, &offset); 1704 + dio->metadata_block = get_metadata_sector_and_offset(ic, area, offset, 1705 + &dio->metadata_offset); 1706 + sector = get_data_sector(ic, area, offset); 1707 + logical_sector = dio->range.logical_sector; 1708 + 1709 + page = mempool_alloc(&ic->recheck_pool, GFP_NOIO); 1710 + buffer = page_to_virt(page); 1711 + 1712 + __bio_for_each_segment(bv, bio, iter, dio->bio_details.bi_iter) { 1713 + unsigned pos = 0; 1714 + 1715 + do { 1716 + char *mem; 1717 + int r; 1718 + struct dm_io_request io_req; 1719 + struct dm_io_region io_loc; 1720 + io_req.bi_opf = REQ_OP_READ; 1721 + io_req.mem.type = DM_IO_KMEM; 1722 + io_req.mem.ptr.addr = buffer; 1723 + io_req.notify.fn = NULL; 1724 + io_req.client = ic->io; 1725 + io_loc.bdev = ic->dev->bdev; 1726 + io_loc.sector = sector; 1727 + io_loc.count = ic->sectors_per_block; 1728 + 1729 + r = dm_io(&io_req, 1, &io_loc, NULL); 1730 + if (unlikely(r)) { 1731 + dio->bi_status = errno_to_blk_status(r); 1732 + goto free_ret; 1733 + } 1734 + 1735 + integrity_sector_checksum(ic, logical_sector, buffer, 1736 + checksum_onstack); 1737 + r = dm_integrity_rw_tag(ic, checksum_onstack, &dio->metadata_block, 1738 + &dio->metadata_offset, ic->tag_size, TAG_CMP); 1739 + if (r) { 1740 + if (r > 0) { 1741 + DMERR_LIMIT("%pg: Checksum failed at sector 0x%llx", 1742 + bio->bi_bdev, logical_sector); 1743 + atomic64_inc(&ic->number_of_mismatches); 1744 + dm_audit_log_bio(DM_MSG_PREFIX, "integrity-checksum", 1745 + bio, logical_sector, 0); 1746 + r = -EILSEQ; 1747 + } 1748 + dio->bi_status = errno_to_blk_status(r); 1749 + goto free_ret; 1750 + } 1751 + 1752 + mem = bvec_kmap_local(&bv); 1753 + memcpy(mem + pos, buffer, ic->sectors_per_block << SECTOR_SHIFT); 1754 + kunmap_local(mem); 1755 + 1756 + pos += ic->sectors_per_block << SECTOR_SHIFT; 1757 + sector += ic->sectors_per_block; 1758 + logical_sector += ic->sectors_per_block; 1759 + } while (pos < bv.bv_len); 1760 + } 1761 + free_ret: 1762 + mempool_free(page, &ic->recheck_pool); 1763 + } 1764 + 1694 1765 static void integrity_metadata(struct work_struct *w) 1695 1766 { 1696 1767 struct dm_integrity_io *dio = container_of(w, struct dm_integrity_io, work); ··· 1851 1776 checksums_ptr - checksums, dio->op == REQ_OP_READ ? TAG_CMP : TAG_WRITE); 1852 1777 if (unlikely(r)) { 1853 1778 if (r > 0) { 1854 - sector_t s; 1855 - 1856 - s = sector - ((r + ic->tag_size - 1) / ic->tag_size); 1857 - DMERR_LIMIT("%pg: Checksum failed at sector 0x%llx", 1858 - bio->bi_bdev, s); 1859 - r = -EILSEQ; 1860 - atomic64_inc(&ic->number_of_mismatches); 1861 - dm_audit_log_bio(DM_MSG_PREFIX, "integrity-checksum", 1862 - bio, s, 0); 1779 + integrity_recheck(dio); 1780 + goto skip_io; 1863 1781 } 1864 1782 if (likely(checksums != checksums_onstack)) 1865 1783 kfree(checksums); ··· 4329 4261 goto bad; 4330 4262 } 4331 4263 4264 + r = mempool_init_page_pool(&ic->recheck_pool, 1, 0); 4265 + if (r) { 4266 + ti->error = "Cannot allocate mempool"; 4267 + goto bad; 4268 + } 4269 + 4332 4270 ic->metadata_wq = alloc_workqueue("dm-integrity-metadata", 4333 4271 WQ_MEM_RECLAIM, METADATA_WORKQUEUE_MAX_ACTIVE); 4334 4272 if (!ic->metadata_wq) { ··· 4683 4609 kvfree(ic->bbs); 4684 4610 if (ic->bufio) 4685 4611 dm_bufio_client_destroy(ic->bufio); 4612 + mempool_exit(&ic->recheck_pool); 4686 4613 mempool_exit(&ic->journal_io_mempool); 4687 4614 if (ic->io) 4688 4615 dm_io_client_destroy(ic->io);