verity: don't call page_address on unmapped highmem pages This fixes a bug that is preventing ARM systems from booting with verity. We were calling page_address on what could potentially have been an unmapped highmem page. Fix is to pass the struct page to the crypto code and let crypto handle the kmap/kunmap. BUG=chromium-os:14089,chrome-os-partner:3287 TEST=Ran dm-verity.git unit tests. Ran platform_DMVerityCorruption on H/W. Verified that ARM now boots. kernel-next.git Review URL: http://codereview.chromium.org/6835032 TBRing. Code has already been reviewed and committed to kernel-next.git There are some trivial changes here to the unittest to match the new list of parameters. Change-Id: Id9e025552aee323e95d50aa9798a964510710fdd Review URL: http://codereview.chromium.org/6851023
diff --git a/dm-bht.c b/dm-bht.c index e3da9c5..72af2a7 100644 --- a/dm-bht.c +++ b/dm-bht.c
@@ -20,6 +20,7 @@ #include <linux/dm-bht.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mm_types.h> #include <linux/scatterlist.h> #include <linux/slab.h> /* k*alloc */ #include <linux/string.h> /* memset */ @@ -95,24 +96,14 @@ /** * dm_bht_compute_hash: hashes a page of data */ -static int dm_bht_compute_hash(struct dm_bht *bht, const void *block, - u8 *digest) +static int dm_bht_compute_hash(struct dm_bht *bht, struct page *pg, + unsigned int offset, u8 *digest) { struct hash_desc *hash_desc = &bht->hash_desc[smp_processor_id()]; struct scatterlist sg; - /* TODO(msb): Once we supporting block_size < PAGE_SIZE, change this to: - * offset_into_page + length < page_size - * For now just check that block is page-aligned. - */ - /* - * TODO(msb): Re-enable once user-space code is modified to use - * aligned buffers. - * BUG_ON(!IS_ALIGNED((uintptr_t)block, PAGE_SIZE)); - */ - sg_init_table(&sg, 1); - sg_set_buf(&sg, block, PAGE_SIZE); + sg_set_page(&sg, pg, PAGE_SIZE, offset); /* Note, this is synchronous. */ if (crypto_hash_init(hash_desc)) { DMCRIT("failed to reinitialize crypto hash (proc:%d)", @@ -653,7 +644,7 @@ * Verifies the path. Returns 0 on ok. */ static int dm_bht_verify_path(struct dm_bht *bht, unsigned int block_index, - const void *block) + struct page *pg, unsigned int offset) { unsigned int depth = bht->depth; struct dm_bht_entry *entry; @@ -674,14 +665,15 @@ BUG_ON(state < DM_BHT_ENTRY_READY); node = dm_bht_get_node(bht, entry, depth, block_index); - if (dm_bht_compute_hash(bht, block, digest) || + if (dm_bht_compute_hash(bht, pg, offset, digest) || dm_bht_compare_hash(bht, digest, node)) goto mismatch; /* Keep the containing block of hashes to be verified in the * next pass. */ - block = entry->nodes; + pg = virt_to_page(entry->nodes); + offset = 0; } while (--depth > 0 && state != DM_BHT_ENTRY_VERIFIED); /* Mark path to leaf as verified. */ @@ -776,7 +768,7 @@ return 1; } - dm_bht_compute_hash(bht, block_data, + dm_bht_compute_hash(bht, virt_to_page(block_data), 0, dm_bht_node(bht, entry, node_index)); return 0; } @@ -846,10 +838,10 @@ if (count == 0) count = bht->node_count; for (j = 0; j < count; j++, child++) { - u8 *block = child->nodes; + struct page *pg = virt_to_page(child->nodes); u8 *digest = dm_bht_node(bht, entry, j); - r = dm_bht_compute_hash(bht, block, digest); + r = dm_bht_compute_hash(bht, pg, 0, digest); if (r) { DMERR("Failed to update (d=%u,i=%u)", depth, i); @@ -1026,10 +1018,12 @@ * should return similarly. */ int dm_bht_verify_block(struct dm_bht *bht, unsigned int block_index, - const void *block) + struct page *pg, unsigned int offset) { int r = 0; + BUG_ON(offset != 0); + /* Make sure that the root has been verified */ if (atomic_read(&bht->root_state) != DM_BHT_ENTRY_VERIFIED) { r = dm_bht_verify_root(bht, dm_bht_compare_hash); @@ -1040,7 +1034,7 @@ } /* Now check levels in between */ - r = dm_bht_verify_path(bht, block_index, block); + r = dm_bht_verify_path(bht, block_index, pg, offset); if (r) DMERR_LIMIT("Failed to verify block: %u (%d)", block_index, r);
diff --git a/dm-bht.h b/dm-bht.h index e7269f6..73809d2 100644 --- a/dm-bht.h +++ b/dm-bht.h
@@ -127,7 +127,7 @@ int dm_bht_populate(struct dm_bht *bht, void *read_cb_ctx, unsigned int block_index); int dm_bht_verify_block(struct dm_bht *bht, unsigned int block_index, - const void *buf); + struct page *pg, unsigned int offset); /* Functions for creating struct dm_bhts on disk. A newly created dm_bht * should not be directly used for verification. (It should be repopulated.)
diff --git a/dm-bht_unittest.cc b/dm-bht_unittest.cc index f0c090c..d7cf61a 100644 --- a/dm-bht_unittest.cc +++ b/dm-bht_unittest.cc
@@ -172,7 +172,8 @@ for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) { DLOG(INFO) << "verifying block: " << blocks; - EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, zero_page)); + EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, + virt_to_page(zero_page), 0)); } EXPECT_EQ(0, dm_bht_destroy(bht_.get())); @@ -195,7 +196,8 @@ for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) { DLOG(INFO) << "verifying block: " << blocks; - EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, zero_page)); + EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, + virt_to_page(zero_page), 0)); } EXPECT_EQ(0, dm_bht_destroy(bht_.get())); @@ -218,7 +220,8 @@ for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) { DLOG(INFO) << "verifying block: " << blocks; - EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, zero_page)); + EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, + virt_to_page(zero_page), 0)); } EXPECT_EQ(0, dm_bht_destroy(bht_.get())); @@ -241,7 +244,8 @@ for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) { DLOG(INFO) << "verifying block: " << blocks; - EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, zero_page)); + EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, + virt_to_page(zero_page), 0)); } EXPECT_EQ(0, dm_bht_destroy(bht_.get())); @@ -264,7 +268,8 @@ for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) { DLOG(INFO) << "verifying block: " << blocks; - EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, zero_page)); + EXPECT_EQ(0, dm_bht_verify_block(bht_.get(), blocks, + virt_to_page(zero_page), 0)); } EXPECT_EQ(0, dm_bht_destroy(bht_.get())); @@ -295,23 +300,25 @@ EXPECT_EQ(dm_bht_store_block(bht_.get(), kBadBlock, bad_hash_block), 0); // Attempt to verify both the bad block and all the neighbors. - EXPECT_LT(dm_bht_verify_block(bht_.get(), kBadBlock + 1, zero_page), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), kBadBlock + 1, + virt_to_page(zero_page), 0), 0); - EXPECT_LT(dm_bht_verify_block(bht_.get(), kBadBlock + 2, zero_page), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), kBadBlock + 2, + virt_to_page(zero_page), 0), 0); EXPECT_LT(dm_bht_verify_block(bht_.get(), kBadBlock + (bht_->node_count / 2), - zero_page), - 0); + virt_to_page(zero_page), 0), 0); - EXPECT_LT(dm_bht_verify_block(bht_.get(), kBadBlock, zero_page), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), kBadBlock, + virt_to_page(zero_page), 0), 0); // Verify that the prior entry is untouched and still safe - EXPECT_EQ(dm_bht_verify_block(bht_.get(), kBadBlock - 1, zero_page), 0); + EXPECT_EQ(dm_bht_verify_block(bht_.get(), kBadBlock - 1, + virt_to_page(zero_page), 0), 0); // Same for the next entry EXPECT_EQ(dm_bht_verify_block(bht_.get(), kBadBlock + bht_->node_count, - zero_page), - 0); + virt_to_page(zero_page), 0), 0); EXPECT_EQ(0, dm_bht_destroy(bht_.get())); free(bad_hash_block); @@ -332,12 +339,12 @@ memset(bad_page, 'A', PAGE_SIZE); - EXPECT_LT(dm_bht_verify_block(bht_.get(), 0, bad_page), 0); - EXPECT_LT(dm_bht_verify_block(bht_.get(), 127, bad_page), 0); - EXPECT_LT(dm_bht_verify_block(bht_.get(), 128, bad_page), 0); - EXPECT_LT(dm_bht_verify_block(bht_.get(), 255, bad_page), 0); - EXPECT_LT(dm_bht_verify_block(bht_.get(), 256, bad_page), 0); - EXPECT_LT(dm_bht_verify_block(bht_.get(), 383, bad_page), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), 0, virt_to_page(bad_page), 0), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), 127, virt_to_page(bad_page), 0), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), 128, virt_to_page(bad_page), 0), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), 255, virt_to_page(bad_page), 0), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), 256, virt_to_page(bad_page), 0), 0); + EXPECT_LT(dm_bht_verify_block(bht_.get(), 383, virt_to_page(bad_page), 0), 0); EXPECT_EQ(0, dm_bht_destroy(bht_.get())); free(bad_page);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/include/linux/mm_types.h