155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
203045cbaSSami Tolvanen /*
303045cbaSSami Tolvanen * Copyright (C) 2012 Red Hat, Inc.
403045cbaSSami Tolvanen *
503045cbaSSami Tolvanen * Author: Mikulas Patocka <mpatocka@redhat.com>
603045cbaSSami Tolvanen *
703045cbaSSami Tolvanen * Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors
803045cbaSSami Tolvanen *
903045cbaSSami Tolvanen * In the file "/sys/module/dm_verity/parameters/prefetch_cluster" you can set
1003045cbaSSami Tolvanen * default prefetch value. Data are read in "prefetch_cluster" chunks from the
1103045cbaSSami Tolvanen * hash device. Setting this greatly improves performance when data and hash
1203045cbaSSami Tolvanen * are on the same disk on different partitions on devices with poor random
1303045cbaSSami Tolvanen * access behavior.
1403045cbaSSami Tolvanen */
1503045cbaSSami Tolvanen
16ffa39380SSami Tolvanen #include "dm-verity.h"
17a739ff3fSSami Tolvanen #include "dm-verity-fec.h"
1888cd3e6cSJaskaran Khurana #include "dm-verity-verify-sig.h"
19074c4466SMichael Weiß #include "dm-audit.h"
2003045cbaSSami Tolvanen #include <linux/module.h>
2103045cbaSSami Tolvanen #include <linux/reboot.h>
2224b83debSChristoph Hellwig #include <linux/scatterlist.h>
23b6c1c574SMatthias Kaehlcke #include <linux/string.h>
24ba2cce82SMike Snitzer #include <linux/jump_label.h>
2503045cbaSSami Tolvanen
2603045cbaSSami Tolvanen #define DM_MSG_PREFIX "verity"
2703045cbaSSami Tolvanen
2803045cbaSSami Tolvanen #define DM_VERITY_ENV_LENGTH 42
2903045cbaSSami Tolvanen #define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR"
3003045cbaSSami Tolvanen
3103045cbaSSami Tolvanen #define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
3203045cbaSSami Tolvanen
3303045cbaSSami Tolvanen #define DM_VERITY_MAX_CORRUPTED_ERRS 100
3403045cbaSSami Tolvanen
3503045cbaSSami Tolvanen #define DM_VERITY_OPT_LOGGING "ignore_corruption"
3603045cbaSSami Tolvanen #define DM_VERITY_OPT_RESTART "restart_on_corruption"
37e1fef0b0SJeongHyeon Lee #define DM_VERITY_OPT_PANIC "panic_on_corruption"
380cc37c2dSSami Tolvanen #define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
39843f38d3SPatrik Torstensson #define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
405721d4e5SNathan Huckleberry #define DM_VERITY_OPT_TASKLET_VERIFY "try_verify_in_tasklet"
4103045cbaSSami Tolvanen
428c22816dSMike Snitzer #define DM_VERITY_OPTS_MAX (4 + DM_VERITY_OPTS_FEC + \
4388cd3e6cSJaskaran Khurana DM_VERITY_ROOT_HASH_VERIFICATION_OPTS)
4403045cbaSSami Tolvanen
4586a3238cSHeinz Mauelshagen static unsigned int dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
4603045cbaSSami Tolvanen
476a808034SHeinz Mauelshagen module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, 0644);
4803045cbaSSami Tolvanen
49ba2cce82SMike Snitzer static DEFINE_STATIC_KEY_FALSE(use_tasklet_enabled);
50ba2cce82SMike Snitzer
5103045cbaSSami Tolvanen struct dm_verity_prefetch_work {
5203045cbaSSami Tolvanen struct work_struct work;
5303045cbaSSami Tolvanen struct dm_verity *v;
5403045cbaSSami Tolvanen sector_t block;
5586a3238cSHeinz Mauelshagen unsigned int n_blocks;
5603045cbaSSami Tolvanen };
5703045cbaSSami Tolvanen
5803045cbaSSami Tolvanen /*
5903045cbaSSami Tolvanen * Auxiliary structure appended to each dm-bufio buffer. If the value
6003045cbaSSami Tolvanen * hash_verified is nonzero, hash of the block has been verified.
6103045cbaSSami Tolvanen *
6203045cbaSSami Tolvanen * The variable hash_verified is set to 0 when allocating the buffer, then
6303045cbaSSami Tolvanen * it can be changed to 1 and it is never reset to 0 again.
6403045cbaSSami Tolvanen *
6503045cbaSSami Tolvanen * There is no lock around this value, a race condition can at worst cause
6603045cbaSSami Tolvanen * that multiple processes verify the hash of the same buffer simultaneously
6703045cbaSSami Tolvanen * and write 1 to hash_verified simultaneously.
6803045cbaSSami Tolvanen * This condition is harmless, so we don't need locking.
6903045cbaSSami Tolvanen */
7003045cbaSSami Tolvanen struct buffer_aux {
7103045cbaSSami Tolvanen int hash_verified;
7203045cbaSSami Tolvanen };
7303045cbaSSami Tolvanen
7403045cbaSSami Tolvanen /*
7503045cbaSSami Tolvanen * Initialize struct buffer_aux for a freshly created buffer.
7603045cbaSSami Tolvanen */
dm_bufio_alloc_callback(struct dm_buffer * buf)7703045cbaSSami Tolvanen static void dm_bufio_alloc_callback(struct dm_buffer *buf)
7803045cbaSSami Tolvanen {
7903045cbaSSami Tolvanen struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
8003045cbaSSami Tolvanen
8103045cbaSSami Tolvanen aux->hash_verified = 0;
8203045cbaSSami Tolvanen }
8303045cbaSSami Tolvanen
8403045cbaSSami Tolvanen /*
8503045cbaSSami Tolvanen * Translate input sector number to the sector number on the target device.
8603045cbaSSami Tolvanen */
verity_map_sector(struct dm_verity * v,sector_t bi_sector)8703045cbaSSami Tolvanen static sector_t verity_map_sector(struct dm_verity *v, sector_t bi_sector)
8803045cbaSSami Tolvanen {
8903045cbaSSami Tolvanen return v->data_start + dm_target_offset(v->ti, bi_sector);
9003045cbaSSami Tolvanen }
9103045cbaSSami Tolvanen
9203045cbaSSami Tolvanen /*
9303045cbaSSami Tolvanen * Return hash position of a specified block at a specified tree level
9403045cbaSSami Tolvanen * (0 is the lowest level).
9503045cbaSSami Tolvanen * The lowest "hash_per_block_bits"-bits of the result denote hash position
9603045cbaSSami Tolvanen * inside a hash block. The remaining bits denote location of the hash block.
9703045cbaSSami Tolvanen */
verity_position_at_level(struct dm_verity * v,sector_t block,int level)9803045cbaSSami Tolvanen static sector_t verity_position_at_level(struct dm_verity *v, sector_t block,
9903045cbaSSami Tolvanen int level)
10003045cbaSSami Tolvanen {
10103045cbaSSami Tolvanen return block >> (level * v->hash_per_block_bits);
10203045cbaSSami Tolvanen }
10303045cbaSSami Tolvanen
verity_hash_update(struct dm_verity * v,struct ahash_request * req,const u8 * data,size_t len,struct crypto_wait * wait)104d1ac3ff0SGilad Ben-Yossef static int verity_hash_update(struct dm_verity *v, struct ahash_request *req,
105d1ac3ff0SGilad Ben-Yossef const u8 *data, size_t len,
10612f1ffc4SGilad Ben-Yossef struct crypto_wait *wait)
107d1ac3ff0SGilad Ben-Yossef {
108d1ac3ff0SGilad Ben-Yossef struct scatterlist sg;
109d1ac3ff0SGilad Ben-Yossef
110e4b069e0SMikulas Patocka if (likely(!is_vmalloc_addr(data))) {
111d1ac3ff0SGilad Ben-Yossef sg_init_one(&sg, data, len);
112d1ac3ff0SGilad Ben-Yossef ahash_request_set_crypt(req, &sg, NULL, len);
11312f1ffc4SGilad Ben-Yossef return crypto_wait_req(crypto_ahash_update(req), wait);
1141c3fe2faSHeinz Mauelshagen }
1151c3fe2faSHeinz Mauelshagen
116e4b069e0SMikulas Patocka do {
117e4b069e0SMikulas Patocka int r;
118e4b069e0SMikulas Patocka size_t this_step = min_t(size_t, len, PAGE_SIZE - offset_in_page(data));
1190ef0b471SHeinz Mauelshagen
120e4b069e0SMikulas Patocka flush_kernel_vmap_range((void *)data, this_step);
121e4b069e0SMikulas Patocka sg_init_table(&sg, 1);
122e4b069e0SMikulas Patocka sg_set_page(&sg, vmalloc_to_page(data), this_step, offset_in_page(data));
123e4b069e0SMikulas Patocka ahash_request_set_crypt(req, &sg, NULL, this_step);
124e4b069e0SMikulas Patocka r = crypto_wait_req(crypto_ahash_update(req), wait);
125e4b069e0SMikulas Patocka if (unlikely(r))
126e4b069e0SMikulas Patocka return r;
127e4b069e0SMikulas Patocka data += this_step;
128e4b069e0SMikulas Patocka len -= this_step;
129e4b069e0SMikulas Patocka } while (len);
1301c3fe2faSHeinz Mauelshagen
131e4b069e0SMikulas Patocka return 0;
132e4b069e0SMikulas Patocka }
133d1ac3ff0SGilad Ben-Yossef
134d1ac3ff0SGilad Ben-Yossef /*
135d1ac3ff0SGilad Ben-Yossef * Wrapper for crypto_ahash_init, which handles verity salting.
136d1ac3ff0SGilad Ben-Yossef */
verity_hash_init(struct dm_verity * v,struct ahash_request * req,struct crypto_wait * wait,bool may_sleep)137d1ac3ff0SGilad Ben-Yossef static int verity_hash_init(struct dm_verity *v, struct ahash_request *req,
13857985252SMikulas Patocka struct crypto_wait *wait, bool may_sleep)
13903045cbaSSami Tolvanen {
14003045cbaSSami Tolvanen int r;
14103045cbaSSami Tolvanen
142d1ac3ff0SGilad Ben-Yossef ahash_request_set_tfm(req, v->tfm);
14357985252SMikulas Patocka ahash_request_set_callback(req,
14457985252SMikulas Patocka may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP | CRYPTO_TFM_REQ_MAY_BACKLOG : 0,
14512f1ffc4SGilad Ben-Yossef crypto_req_done, (void *)wait);
14612f1ffc4SGilad Ben-Yossef crypto_init_wait(wait);
14703045cbaSSami Tolvanen
14812f1ffc4SGilad Ben-Yossef r = crypto_wait_req(crypto_ahash_init(req), wait);
14903045cbaSSami Tolvanen
15003045cbaSSami Tolvanen if (unlikely(r < 0)) {
15157985252SMikulas Patocka if (r != -ENOMEM)
152d1ac3ff0SGilad Ben-Yossef DMERR("crypto_ahash_init failed: %d", r);
15303045cbaSSami Tolvanen return r;
15403045cbaSSami Tolvanen }
15503045cbaSSami Tolvanen
156f52236e0SGilad Ben-Yossef if (likely(v->salt_size && (v->version >= 1)))
15712f1ffc4SGilad Ben-Yossef r = verity_hash_update(v, req, v->salt, v->salt_size, wait);
15803045cbaSSami Tolvanen
15903045cbaSSami Tolvanen return r;
16003045cbaSSami Tolvanen }
16103045cbaSSami Tolvanen
verity_hash_final(struct dm_verity * v,struct ahash_request * req,u8 * digest,struct crypto_wait * wait)162d1ac3ff0SGilad Ben-Yossef static int verity_hash_final(struct dm_verity *v, struct ahash_request *req,
16312f1ffc4SGilad Ben-Yossef u8 *digest, struct crypto_wait *wait)
16403045cbaSSami Tolvanen {
16503045cbaSSami Tolvanen int r;
16603045cbaSSami Tolvanen
167f52236e0SGilad Ben-Yossef if (unlikely(v->salt_size && (!v->version))) {
16812f1ffc4SGilad Ben-Yossef r = verity_hash_update(v, req, v->salt, v->salt_size, wait);
16903045cbaSSami Tolvanen
17003045cbaSSami Tolvanen if (r < 0) {
1711c131886SHeinz Mauelshagen DMERR("%s failed updating salt: %d", __func__, r);
172d1ac3ff0SGilad Ben-Yossef goto out;
17303045cbaSSami Tolvanen }
17403045cbaSSami Tolvanen }
17503045cbaSSami Tolvanen
176d1ac3ff0SGilad Ben-Yossef ahash_request_set_crypt(req, NULL, digest, 0);
17712f1ffc4SGilad Ben-Yossef r = crypto_wait_req(crypto_ahash_final(req), wait);
178d1ac3ff0SGilad Ben-Yossef out:
17903045cbaSSami Tolvanen return r;
18003045cbaSSami Tolvanen }
18103045cbaSSami Tolvanen
verity_hash(struct dm_verity * v,struct ahash_request * req,const u8 * data,size_t len,u8 * digest,bool may_sleep)182d1ac3ff0SGilad Ben-Yossef int verity_hash(struct dm_verity *v, struct ahash_request *req,
18357985252SMikulas Patocka const u8 *data, size_t len, u8 *digest, bool may_sleep)
18403045cbaSSami Tolvanen {
18503045cbaSSami Tolvanen int r;
18612f1ffc4SGilad Ben-Yossef struct crypto_wait wait;
18703045cbaSSami Tolvanen
18857985252SMikulas Patocka r = verity_hash_init(v, req, &wait, may_sleep);
18903045cbaSSami Tolvanen if (unlikely(r < 0))
190d1ac3ff0SGilad Ben-Yossef goto out;
19103045cbaSSami Tolvanen
19212f1ffc4SGilad Ben-Yossef r = verity_hash_update(v, req, data, len, &wait);
19303045cbaSSami Tolvanen if (unlikely(r < 0))
194d1ac3ff0SGilad Ben-Yossef goto out;
19503045cbaSSami Tolvanen
19612f1ffc4SGilad Ben-Yossef r = verity_hash_final(v, req, digest, &wait);
197d1ac3ff0SGilad Ben-Yossef
198d1ac3ff0SGilad Ben-Yossef out:
199d1ac3ff0SGilad Ben-Yossef return r;
20003045cbaSSami Tolvanen }
20103045cbaSSami Tolvanen
verity_hash_at_level(struct dm_verity * v,sector_t block,int level,sector_t * hash_block,unsigned int * offset)20203045cbaSSami Tolvanen static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
20386a3238cSHeinz Mauelshagen sector_t *hash_block, unsigned int *offset)
20403045cbaSSami Tolvanen {
20503045cbaSSami Tolvanen sector_t position = verity_position_at_level(v, block, level);
20686a3238cSHeinz Mauelshagen unsigned int idx;
20703045cbaSSami Tolvanen
20803045cbaSSami Tolvanen *hash_block = v->hash_level_block[level] + (position >> v->hash_per_block_bits);
20903045cbaSSami Tolvanen
21003045cbaSSami Tolvanen if (!offset)
21103045cbaSSami Tolvanen return;
21203045cbaSSami Tolvanen
21303045cbaSSami Tolvanen idx = position & ((1 << v->hash_per_block_bits) - 1);
21403045cbaSSami Tolvanen if (!v->version)
21503045cbaSSami Tolvanen *offset = idx * v->digest_size;
21603045cbaSSami Tolvanen else
21703045cbaSSami Tolvanen *offset = idx << (v->hash_dev_block_bits - v->hash_per_block_bits);
21803045cbaSSami Tolvanen }
21903045cbaSSami Tolvanen
22003045cbaSSami Tolvanen /*
22103045cbaSSami Tolvanen * Handle verification errors.
22203045cbaSSami Tolvanen */
verity_handle_err(struct dm_verity * v,enum verity_block_type type,unsigned long long block)22303045cbaSSami Tolvanen static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
22403045cbaSSami Tolvanen unsigned long long block)
22503045cbaSSami Tolvanen {
22603045cbaSSami Tolvanen char verity_env[DM_VERITY_ENV_LENGTH];
22703045cbaSSami Tolvanen char *envp[] = { verity_env, NULL };
22803045cbaSSami Tolvanen const char *type_str = "";
22903045cbaSSami Tolvanen struct mapped_device *md = dm_table_get_md(v->ti->table);
23003045cbaSSami Tolvanen
23103045cbaSSami Tolvanen /* Corruption should be visible in device status in all modes */
2325721d4e5SNathan Huckleberry v->hash_failed = true;
23303045cbaSSami Tolvanen
23403045cbaSSami Tolvanen if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
23503045cbaSSami Tolvanen goto out;
23603045cbaSSami Tolvanen
23703045cbaSSami Tolvanen v->corrupted_errs++;
23803045cbaSSami Tolvanen
23903045cbaSSami Tolvanen switch (type) {
24003045cbaSSami Tolvanen case DM_VERITY_BLOCK_TYPE_DATA:
24103045cbaSSami Tolvanen type_str = "data";
24203045cbaSSami Tolvanen break;
24303045cbaSSami Tolvanen case DM_VERITY_BLOCK_TYPE_METADATA:
24403045cbaSSami Tolvanen type_str = "metadata";
24503045cbaSSami Tolvanen break;
24603045cbaSSami Tolvanen default:
24703045cbaSSami Tolvanen BUG();
24803045cbaSSami Tolvanen }
24903045cbaSSami Tolvanen
2502eba4e64SMilan Broz DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name,
2512eba4e64SMilan Broz type_str, block);
25203045cbaSSami Tolvanen
253074c4466SMichael Weiß if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS) {
25403045cbaSSami Tolvanen DMERR("%s: reached maximum errors", v->data_dev->name);
255074c4466SMichael Weiß dm_audit_log_target(DM_MSG_PREFIX, "max-corrupted-errors", v->ti, 0);
256074c4466SMichael Weiß }
25703045cbaSSami Tolvanen
25803045cbaSSami Tolvanen snprintf(verity_env, DM_VERITY_ENV_LENGTH, "%s=%d,%llu",
25903045cbaSSami Tolvanen DM_VERITY_ENV_VAR_NAME, type, block);
26003045cbaSSami Tolvanen
26103045cbaSSami Tolvanen kobject_uevent_env(&disk_to_dev(dm_disk(md))->kobj, KOBJ_CHANGE, envp);
26203045cbaSSami Tolvanen
26303045cbaSSami Tolvanen out:
26403045cbaSSami Tolvanen if (v->mode == DM_VERITY_MODE_LOGGING)
26503045cbaSSami Tolvanen return 0;
26603045cbaSSami Tolvanen
267*cada2646SMikulas Patocka if (v->mode == DM_VERITY_MODE_RESTART)
268*cada2646SMikulas Patocka kernel_restart("dm-verity device corrupted");
26903045cbaSSami Tolvanen
270e1fef0b0SJeongHyeon Lee if (v->mode == DM_VERITY_MODE_PANIC)
271e1fef0b0SJeongHyeon Lee panic("dm-verity device corrupted");
272e1fef0b0SJeongHyeon Lee
27303045cbaSSami Tolvanen return 1;
27403045cbaSSami Tolvanen }
27503045cbaSSami Tolvanen
27603045cbaSSami Tolvanen /*
27703045cbaSSami Tolvanen * Verify hash of a metadata block pertaining to the specified data block
27803045cbaSSami Tolvanen * ("block" argument) at a specified level ("level" argument).
27903045cbaSSami Tolvanen *
280ffa39380SSami Tolvanen * On successful return, verity_io_want_digest(v, io) contains the hash value
281ffa39380SSami Tolvanen * for a lower tree level or for the data block (if we're at the lowest level).
28203045cbaSSami Tolvanen *
28303045cbaSSami Tolvanen * If "skip_unverified" is true, unverified buffer is skipped and 1 is returned.
28403045cbaSSami Tolvanen * If "skip_unverified" is false, unverified buffer is hashed and verified
285ffa39380SSami Tolvanen * against current value of verity_io_want_digest(v, io).
28603045cbaSSami Tolvanen */
verity_verify_level(struct dm_verity * v,struct dm_verity_io * io,sector_t block,int level,bool skip_unverified,u8 * want_digest)28703045cbaSSami Tolvanen static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
28803045cbaSSami Tolvanen sector_t block, int level, bool skip_unverified,
28903045cbaSSami Tolvanen u8 *want_digest)
29003045cbaSSami Tolvanen {
29103045cbaSSami Tolvanen struct dm_buffer *buf;
29203045cbaSSami Tolvanen struct buffer_aux *aux;
29303045cbaSSami Tolvanen u8 *data;
29403045cbaSSami Tolvanen int r;
29503045cbaSSami Tolvanen sector_t hash_block;
29686a3238cSHeinz Mauelshagen unsigned int offset;
29703045cbaSSami Tolvanen
29803045cbaSSami Tolvanen verity_hash_at_level(v, block, level, &hash_block, &offset);
29903045cbaSSami Tolvanen
300ba2cce82SMike Snitzer if (static_branch_unlikely(&use_tasklet_enabled) && io->in_tasklet) {
3015721d4e5SNathan Huckleberry data = dm_bufio_get(v->bufio, hash_block, &buf);
3025721d4e5SNathan Huckleberry if (data == NULL) {
3035721d4e5SNathan Huckleberry /*
3045721d4e5SNathan Huckleberry * In tasklet and the hash was not in the bufio cache.
3055721d4e5SNathan Huckleberry * Return early and resume execution from a work-queue
3065721d4e5SNathan Huckleberry * to read the hash from disk.
3075721d4e5SNathan Huckleberry */
3085721d4e5SNathan Huckleberry return -EAGAIN;
3095721d4e5SNathan Huckleberry }
3105721d4e5SNathan Huckleberry } else
31103045cbaSSami Tolvanen data = dm_bufio_read(v->bufio, hash_block, &buf);
3125721d4e5SNathan Huckleberry
31303045cbaSSami Tolvanen if (IS_ERR(data))
31403045cbaSSami Tolvanen return PTR_ERR(data);
31503045cbaSSami Tolvanen
31603045cbaSSami Tolvanen aux = dm_bufio_get_aux_data(buf);
31703045cbaSSami Tolvanen
31803045cbaSSami Tolvanen if (!aux->hash_verified) {
31903045cbaSSami Tolvanen if (skip_unverified) {
32003045cbaSSami Tolvanen r = 1;
32103045cbaSSami Tolvanen goto release_ret_r;
32203045cbaSSami Tolvanen }
32303045cbaSSami Tolvanen
324d1ac3ff0SGilad Ben-Yossef r = verity_hash(v, verity_io_hash_req(v, io),
32503045cbaSSami Tolvanen data, 1 << v->hash_dev_block_bits,
32657985252SMikulas Patocka verity_io_real_digest(v, io), !io->in_tasklet);
32703045cbaSSami Tolvanen if (unlikely(r < 0))
32803045cbaSSami Tolvanen goto release_ret_r;
32903045cbaSSami Tolvanen
330ffa39380SSami Tolvanen if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
33103045cbaSSami Tolvanen v->digest_size) == 0))
33203045cbaSSami Tolvanen aux->hash_verified = 1;
333ba2cce82SMike Snitzer else if (static_branch_unlikely(&use_tasklet_enabled) &&
334ba2cce82SMike Snitzer io->in_tasklet) {
3355721d4e5SNathan Huckleberry /*
3365721d4e5SNathan Huckleberry * Error handling code (FEC included) cannot be run in a
3375721d4e5SNathan Huckleberry * tasklet since it may sleep, so fallback to work-queue.
3385721d4e5SNathan Huckleberry */
3395721d4e5SNathan Huckleberry r = -EAGAIN;
3405721d4e5SNathan Huckleberry goto release_ret_r;
34103b18887SHeinz Mauelshagen } else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_METADATA,
342a739ff3fSSami Tolvanen hash_block, data, NULL) == 0)
343a739ff3fSSami Tolvanen aux->hash_verified = 1;
34403045cbaSSami Tolvanen else if (verity_handle_err(v,
34503045cbaSSami Tolvanen DM_VERITY_BLOCK_TYPE_METADATA,
34603045cbaSSami Tolvanen hash_block)) {
347074c4466SMichael Weiß struct bio *bio =
348074c4466SMichael Weiß dm_bio_from_per_bio_data(io,
349074c4466SMichael Weiß v->ti->per_io_data_size);
350074c4466SMichael Weiß dm_audit_log_bio(DM_MSG_PREFIX, "verify-metadata", bio,
351074c4466SMichael Weiß block, 0);
35203045cbaSSami Tolvanen r = -EIO;
35303045cbaSSami Tolvanen goto release_ret_r;
35403045cbaSSami Tolvanen }
35503045cbaSSami Tolvanen }
35603045cbaSSami Tolvanen
35703045cbaSSami Tolvanen data += offset;
35803045cbaSSami Tolvanen memcpy(want_digest, data, v->digest_size);
35903045cbaSSami Tolvanen r = 0;
36003045cbaSSami Tolvanen
36103045cbaSSami Tolvanen release_ret_r:
36203045cbaSSami Tolvanen dm_bufio_release(buf);
36303045cbaSSami Tolvanen return r;
36403045cbaSSami Tolvanen }
36503045cbaSSami Tolvanen
36603045cbaSSami Tolvanen /*
36703045cbaSSami Tolvanen * Find a hash for a given block, write it to digest and verify the integrity
36803045cbaSSami Tolvanen * of the hash tree if necessary.
36903045cbaSSami Tolvanen */
verity_hash_for_block(struct dm_verity * v,struct dm_verity_io * io,sector_t block,u8 * digest,bool * is_zero)370ffa39380SSami Tolvanen int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
3710cc37c2dSSami Tolvanen sector_t block, u8 *digest, bool *is_zero)
37203045cbaSSami Tolvanen {
3730cc37c2dSSami Tolvanen int r = 0, i;
37403045cbaSSami Tolvanen
37503045cbaSSami Tolvanen if (likely(v->levels)) {
37603045cbaSSami Tolvanen /*
37703045cbaSSami Tolvanen * First, we try to get the requested hash for
37803045cbaSSami Tolvanen * the current block. If the hash block itself is
37903045cbaSSami Tolvanen * verified, zero is returned. If it isn't, this
38003045cbaSSami Tolvanen * function returns 1 and we fall back to whole
38103045cbaSSami Tolvanen * chain verification.
38203045cbaSSami Tolvanen */
38303045cbaSSami Tolvanen r = verity_verify_level(v, io, block, 0, true, digest);
38403045cbaSSami Tolvanen if (likely(r <= 0))
3850cc37c2dSSami Tolvanen goto out;
38603045cbaSSami Tolvanen }
38703045cbaSSami Tolvanen
38803045cbaSSami Tolvanen memcpy(digest, v->root_digest, v->digest_size);
38903045cbaSSami Tolvanen
39003045cbaSSami Tolvanen for (i = v->levels - 1; i >= 0; i--) {
39103045cbaSSami Tolvanen r = verity_verify_level(v, io, block, i, false, digest);
39203045cbaSSami Tolvanen if (unlikely(r))
3930cc37c2dSSami Tolvanen goto out;
39403045cbaSSami Tolvanen }
3950cc37c2dSSami Tolvanen out:
3960cc37c2dSSami Tolvanen if (!r && v->zero_digest)
3970cc37c2dSSami Tolvanen *is_zero = !memcmp(v->zero_digest, digest, v->digest_size);
3980cc37c2dSSami Tolvanen else
3990cc37c2dSSami Tolvanen *is_zero = false;
40003045cbaSSami Tolvanen
4010cc37c2dSSami Tolvanen return r;
40203045cbaSSami Tolvanen }
40303045cbaSSami Tolvanen
40403045cbaSSami Tolvanen /*
405d1ac3ff0SGilad Ben-Yossef * Calculates the digest for the given bio
406d1ac3ff0SGilad Ben-Yossef */
verity_for_io_block(struct dm_verity * v,struct dm_verity_io * io,struct bvec_iter * iter,struct crypto_wait * wait)407d4b1aaf5Sweiyongjun (A) static int verity_for_io_block(struct dm_verity *v, struct dm_verity_io *io,
40812f1ffc4SGilad Ben-Yossef struct bvec_iter *iter, struct crypto_wait *wait)
409d1ac3ff0SGilad Ben-Yossef {
410d1ac3ff0SGilad Ben-Yossef unsigned int todo = 1 << v->data_dev_block_bits;
411d1ac3ff0SGilad Ben-Yossef struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
412d1ac3ff0SGilad Ben-Yossef struct scatterlist sg;
413d1ac3ff0SGilad Ben-Yossef struct ahash_request *req = verity_io_hash_req(v, io);
414d1ac3ff0SGilad Ben-Yossef
415d1ac3ff0SGilad Ben-Yossef do {
416d1ac3ff0SGilad Ben-Yossef int r;
417d1ac3ff0SGilad Ben-Yossef unsigned int len;
418d1ac3ff0SGilad Ben-Yossef struct bio_vec bv = bio_iter_iovec(bio, *iter);
419d1ac3ff0SGilad Ben-Yossef
420d1ac3ff0SGilad Ben-Yossef sg_init_table(&sg, 1);
421d1ac3ff0SGilad Ben-Yossef
422d1ac3ff0SGilad Ben-Yossef len = bv.bv_len;
423d1ac3ff0SGilad Ben-Yossef
424d1ac3ff0SGilad Ben-Yossef if (likely(len >= todo))
425d1ac3ff0SGilad Ben-Yossef len = todo;
426d1ac3ff0SGilad Ben-Yossef /*
427d1ac3ff0SGilad Ben-Yossef * Operating on a single page at a time looks suboptimal
428d1ac3ff0SGilad Ben-Yossef * until you consider the typical block size is 4,096B.
429d1ac3ff0SGilad Ben-Yossef * Going through this loops twice should be very rare.
430d1ac3ff0SGilad Ben-Yossef */
431d1ac3ff0SGilad Ben-Yossef sg_set_page(&sg, bv.bv_page, len, bv.bv_offset);
432d1ac3ff0SGilad Ben-Yossef ahash_request_set_crypt(req, &sg, NULL, len);
43312f1ffc4SGilad Ben-Yossef r = crypto_wait_req(crypto_ahash_update(req), wait);
434d1ac3ff0SGilad Ben-Yossef
435d1ac3ff0SGilad Ben-Yossef if (unlikely(r < 0)) {
4361c131886SHeinz Mauelshagen DMERR("%s crypto op failed: %d", __func__, r);
437d1ac3ff0SGilad Ben-Yossef return r;
438d1ac3ff0SGilad Ben-Yossef }
439d1ac3ff0SGilad Ben-Yossef
440d1ac3ff0SGilad Ben-Yossef bio_advance_iter(bio, iter, len);
441d1ac3ff0SGilad Ben-Yossef todo -= len;
442d1ac3ff0SGilad Ben-Yossef } while (todo);
443d1ac3ff0SGilad Ben-Yossef
444d1ac3ff0SGilad Ben-Yossef return 0;
445d1ac3ff0SGilad Ben-Yossef }
446d1ac3ff0SGilad Ben-Yossef
447d1ac3ff0SGilad Ben-Yossef /*
448bb4d73acSSami Tolvanen * Calls function process for 1 << v->data_dev_block_bits bytes in the bio_vec
449bb4d73acSSami Tolvanen * starting from iter.
450bb4d73acSSami Tolvanen */
verity_for_bv_block(struct dm_verity * v,struct dm_verity_io * io,struct bvec_iter * iter,int (* process)(struct dm_verity * v,struct dm_verity_io * io,u8 * data,size_t len))451bb4d73acSSami Tolvanen int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
452bb4d73acSSami Tolvanen struct bvec_iter *iter,
453bb4d73acSSami Tolvanen int (*process)(struct dm_verity *v,
454bb4d73acSSami Tolvanen struct dm_verity_io *io, u8 *data,
455bb4d73acSSami Tolvanen size_t len))
456bb4d73acSSami Tolvanen {
45786a3238cSHeinz Mauelshagen unsigned int todo = 1 << v->data_dev_block_bits;
45830187e1dSMike Snitzer struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
459bb4d73acSSami Tolvanen
460bb4d73acSSami Tolvanen do {
461bb4d73acSSami Tolvanen int r;
462bb4d73acSSami Tolvanen u8 *page;
46386a3238cSHeinz Mauelshagen unsigned int len;
464bb4d73acSSami Tolvanen struct bio_vec bv = bio_iter_iovec(bio, *iter);
465bb4d73acSSami Tolvanen
46630495e68SChristoph Hellwig page = bvec_kmap_local(&bv);
467bb4d73acSSami Tolvanen len = bv.bv_len;
468bb4d73acSSami Tolvanen
469bb4d73acSSami Tolvanen if (likely(len >= todo))
470bb4d73acSSami Tolvanen len = todo;
471bb4d73acSSami Tolvanen
47230495e68SChristoph Hellwig r = process(v, io, page, len);
47330495e68SChristoph Hellwig kunmap_local(page);
474bb4d73acSSami Tolvanen
475bb4d73acSSami Tolvanen if (r < 0)
476bb4d73acSSami Tolvanen return r;
477bb4d73acSSami Tolvanen
478bb4d73acSSami Tolvanen bio_advance_iter(bio, iter, len);
479bb4d73acSSami Tolvanen todo -= len;
480bb4d73acSSami Tolvanen } while (todo);
481bb4d73acSSami Tolvanen
482bb4d73acSSami Tolvanen return 0;
483bb4d73acSSami Tolvanen }
484bb4d73acSSami Tolvanen
verity_recheck_copy(struct dm_verity * v,struct dm_verity_io * io,u8 * data,size_t len)485e5cc2309SMikulas Patocka static int verity_recheck_copy(struct dm_verity *v, struct dm_verity_io *io,
486e5cc2309SMikulas Patocka u8 *data, size_t len)
487e5cc2309SMikulas Patocka {
488e5cc2309SMikulas Patocka memcpy(data, io->recheck_buffer, len);
489e5cc2309SMikulas Patocka io->recheck_buffer += len;
490e5cc2309SMikulas Patocka
491e5cc2309SMikulas Patocka return 0;
492e5cc2309SMikulas Patocka }
493e5cc2309SMikulas Patocka
verity_recheck(struct dm_verity * v,struct dm_verity_io * io,struct bvec_iter start,sector_t cur_block)494763f1f13SArnd Bergmann static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io,
495e5cc2309SMikulas Patocka struct bvec_iter start, sector_t cur_block)
496e5cc2309SMikulas Patocka {
497e5cc2309SMikulas Patocka struct page *page;
498e5cc2309SMikulas Patocka void *buffer;
499e5cc2309SMikulas Patocka int r;
500e5cc2309SMikulas Patocka struct dm_io_request io_req;
501e5cc2309SMikulas Patocka struct dm_io_region io_loc;
502e5cc2309SMikulas Patocka
503e5cc2309SMikulas Patocka page = mempool_alloc(&v->recheck_pool, GFP_NOIO);
504e5cc2309SMikulas Patocka buffer = page_to_virt(page);
505e5cc2309SMikulas Patocka
506e5cc2309SMikulas Patocka io_req.bi_opf = REQ_OP_READ;
507e5cc2309SMikulas Patocka io_req.mem.type = DM_IO_KMEM;
508e5cc2309SMikulas Patocka io_req.mem.ptr.addr = buffer;
509e5cc2309SMikulas Patocka io_req.notify.fn = NULL;
510e5cc2309SMikulas Patocka io_req.client = v->io;
511e5cc2309SMikulas Patocka io_loc.bdev = v->data_dev->bdev;
512e5cc2309SMikulas Patocka io_loc.sector = cur_block << (v->data_dev_block_bits - SECTOR_SHIFT);
513e5cc2309SMikulas Patocka io_loc.count = 1 << (v->data_dev_block_bits - SECTOR_SHIFT);
5145cfcea64SHongyu Jin r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
515e5cc2309SMikulas Patocka if (unlikely(r))
516e5cc2309SMikulas Patocka goto free_ret;
517e5cc2309SMikulas Patocka
518e5cc2309SMikulas Patocka r = verity_hash(v, verity_io_hash_req(v, io), buffer,
519e5cc2309SMikulas Patocka 1 << v->data_dev_block_bits,
520e5cc2309SMikulas Patocka verity_io_real_digest(v, io), true);
521e5cc2309SMikulas Patocka if (unlikely(r))
522e5cc2309SMikulas Patocka goto free_ret;
523e5cc2309SMikulas Patocka
524e5cc2309SMikulas Patocka if (memcmp(verity_io_real_digest(v, io),
525e5cc2309SMikulas Patocka verity_io_want_digest(v, io), v->digest_size)) {
526e5cc2309SMikulas Patocka r = -EIO;
527e5cc2309SMikulas Patocka goto free_ret;
528e5cc2309SMikulas Patocka }
529e5cc2309SMikulas Patocka
530e5cc2309SMikulas Patocka io->recheck_buffer = buffer;
531e5cc2309SMikulas Patocka r = verity_for_bv_block(v, io, &start, verity_recheck_copy);
532e5cc2309SMikulas Patocka if (unlikely(r))
533e5cc2309SMikulas Patocka goto free_ret;
534e5cc2309SMikulas Patocka
535e5cc2309SMikulas Patocka r = 0;
536e5cc2309SMikulas Patocka free_ret:
537e5cc2309SMikulas Patocka mempool_free(page, &v->recheck_pool);
538e5cc2309SMikulas Patocka
539e5cc2309SMikulas Patocka return r;
540e5cc2309SMikulas Patocka }
541e5cc2309SMikulas Patocka
verity_bv_zero(struct dm_verity * v,struct dm_verity_io * io,u8 * data,size_t len)5420cc37c2dSSami Tolvanen static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
5430cc37c2dSSami Tolvanen u8 *data, size_t len)
5440cc37c2dSSami Tolvanen {
5450cc37c2dSSami Tolvanen memset(data, 0, len);
5460cc37c2dSSami Tolvanen return 0;
5470cc37c2dSSami Tolvanen }
5480cc37c2dSSami Tolvanen
549bb4d73acSSami Tolvanen /*
550843f38d3SPatrik Torstensson * Moves the bio iter one data block forward.
551843f38d3SPatrik Torstensson */
verity_bv_skip_block(struct dm_verity * v,struct dm_verity_io * io,struct bvec_iter * iter)552843f38d3SPatrik Torstensson static inline void verity_bv_skip_block(struct dm_verity *v,
553843f38d3SPatrik Torstensson struct dm_verity_io *io,
554843f38d3SPatrik Torstensson struct bvec_iter *iter)
555843f38d3SPatrik Torstensson {
556843f38d3SPatrik Torstensson struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
557843f38d3SPatrik Torstensson
558843f38d3SPatrik Torstensson bio_advance_iter(bio, iter, 1 << v->data_dev_block_bits);
559843f38d3SPatrik Torstensson }
560843f38d3SPatrik Torstensson
561843f38d3SPatrik Torstensson /*
56203045cbaSSami Tolvanen * Verify one "dm_verity_io" structure.
56303045cbaSSami Tolvanen */
verity_verify_io(struct dm_verity_io * io)56403045cbaSSami Tolvanen static int verity_verify_io(struct dm_verity_io *io)
56503045cbaSSami Tolvanen {
5660cc37c2dSSami Tolvanen bool is_zero;
56703045cbaSSami Tolvanen struct dm_verity *v = io->v;
568bb4d73acSSami Tolvanen struct bvec_iter start;
569e9307e3dSMike Snitzer struct bvec_iter iter_copy;
570e9307e3dSMike Snitzer struct bvec_iter *iter;
57112f1ffc4SGilad Ben-Yossef struct crypto_wait wait;
5722c0468e0SAkilesh Kailash struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
5735721d4e5SNathan Huckleberry unsigned int b;
57403045cbaSSami Tolvanen
575e9307e3dSMike Snitzer if (static_branch_unlikely(&use_tasklet_enabled) && io->in_tasklet) {
576e9307e3dSMike Snitzer /*
577e9307e3dSMike Snitzer * Copy the iterator in case we need to restart
578e9307e3dSMike Snitzer * verification in a work-queue.
579e9307e3dSMike Snitzer */
580e9307e3dSMike Snitzer iter_copy = io->iter;
581e9307e3dSMike Snitzer iter = &iter_copy;
582e9307e3dSMike Snitzer } else
583e9307e3dSMike Snitzer iter = &io->iter;
58403045cbaSSami Tolvanen
58503045cbaSSami Tolvanen for (b = 0; b < io->n_blocks; b++) {
58603045cbaSSami Tolvanen int r;
587843f38d3SPatrik Torstensson sector_t cur_block = io->block + b;
588d1ac3ff0SGilad Ben-Yossef struct ahash_request *req = verity_io_hash_req(v, io);
58903045cbaSSami Tolvanen
590e8c5d45fSYeongjin Gil if (v->validated_blocks && bio->bi_status == BLK_STS_OK &&
591843f38d3SPatrik Torstensson likely(test_bit(cur_block, v->validated_blocks))) {
592e9307e3dSMike Snitzer verity_bv_skip_block(v, io, iter);
593843f38d3SPatrik Torstensson continue;
594843f38d3SPatrik Torstensson }
595843f38d3SPatrik Torstensson
596843f38d3SPatrik Torstensson r = verity_hash_for_block(v, io, cur_block,
5970cc37c2dSSami Tolvanen verity_io_want_digest(v, io),
5980cc37c2dSSami Tolvanen &is_zero);
59903045cbaSSami Tolvanen if (unlikely(r < 0))
60003045cbaSSami Tolvanen return r;
60103045cbaSSami Tolvanen
6020cc37c2dSSami Tolvanen if (is_zero) {
6030cc37c2dSSami Tolvanen /*
6040cc37c2dSSami Tolvanen * If we expect a zero block, don't validate, just
6050cc37c2dSSami Tolvanen * return zeros.
6060cc37c2dSSami Tolvanen */
607e9307e3dSMike Snitzer r = verity_for_bv_block(v, io, iter,
6080cc37c2dSSami Tolvanen verity_bv_zero);
6090cc37c2dSSami Tolvanen if (unlikely(r < 0))
6100cc37c2dSSami Tolvanen return r;
6110cc37c2dSSami Tolvanen
6120cc37c2dSSami Tolvanen continue;
6130cc37c2dSSami Tolvanen }
6140cc37c2dSSami Tolvanen
61557985252SMikulas Patocka r = verity_hash_init(v, req, &wait, !io->in_tasklet);
61603045cbaSSami Tolvanen if (unlikely(r < 0))
61703045cbaSSami Tolvanen return r;
61803045cbaSSami Tolvanen
619e9307e3dSMike Snitzer start = *iter;
620e9307e3dSMike Snitzer r = verity_for_io_block(v, io, iter, &wait);
62103045cbaSSami Tolvanen if (unlikely(r < 0))
62203045cbaSSami Tolvanen return r;
62303045cbaSSami Tolvanen
624d1ac3ff0SGilad Ben-Yossef r = verity_hash_final(v, req, verity_io_real_digest(v, io),
62512f1ffc4SGilad Ben-Yossef &wait);
62603045cbaSSami Tolvanen if (unlikely(r < 0))
62703045cbaSSami Tolvanen return r;
62803045cbaSSami Tolvanen
629ffa39380SSami Tolvanen if (likely(memcmp(verity_io_real_digest(v, io),
630843f38d3SPatrik Torstensson verity_io_want_digest(v, io), v->digest_size) == 0)) {
631843f38d3SPatrik Torstensson if (v->validated_blocks)
632843f38d3SPatrik Torstensson set_bit(cur_block, v->validated_blocks);
63303045cbaSSami Tolvanen continue;
634ba2cce82SMike Snitzer } else if (static_branch_unlikely(&use_tasklet_enabled) &&
635ba2cce82SMike Snitzer io->in_tasklet) {
6365721d4e5SNathan Huckleberry /*
6375721d4e5SNathan Huckleberry * Error handling code (FEC included) cannot be run in a
6385721d4e5SNathan Huckleberry * tasklet since it may sleep, so fallback to work-queue.
6395721d4e5SNathan Huckleberry */
6405721d4e5SNathan Huckleberry return -EAGAIN;
641e5cc2309SMikulas Patocka } else if (verity_recheck(v, io, start, cur_block) == 0) {
642e5cc2309SMikulas Patocka if (v->validated_blocks)
643e5cc2309SMikulas Patocka set_bit(cur_block, v->validated_blocks);
644e5cc2309SMikulas Patocka continue;
6450a36463fSMike Snitzer #if defined(CONFIG_DM_VERITY_FEC)
64620e6fc85SJeongHyeon Lee } else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
64720e6fc85SJeongHyeon Lee cur_block, NULL, &start) == 0) {
648a739ff3fSSami Tolvanen continue;
6490a36463fSMike Snitzer #endif
65020e6fc85SJeongHyeon Lee } else {
6512c0468e0SAkilesh Kailash if (bio->bi_status) {
6522c0468e0SAkilesh Kailash /*
6532c0468e0SAkilesh Kailash * Error correction failed; Just return error
6542c0468e0SAkilesh Kailash */
6552c0468e0SAkilesh Kailash return -EIO;
6562c0468e0SAkilesh Kailash }
6572c0468e0SAkilesh Kailash if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
658074c4466SMichael Weiß cur_block)) {
659074c4466SMichael Weiß dm_audit_log_bio(DM_MSG_PREFIX, "verify-data",
660074c4466SMichael Weiß bio, cur_block, 0);
66103045cbaSSami Tolvanen return -EIO;
66203045cbaSSami Tolvanen }
6632c0468e0SAkilesh Kailash }
664074c4466SMichael Weiß }
66503045cbaSSami Tolvanen
66603045cbaSSami Tolvanen return 0;
66703045cbaSSami Tolvanen }
66803045cbaSSami Tolvanen
66903045cbaSSami Tolvanen /*
670252bd125SHyeongseok Kim * Skip verity work in response to I/O error when system is shutting down.
671252bd125SHyeongseok Kim */
verity_is_system_shutting_down(void)672252bd125SHyeongseok Kim static inline bool verity_is_system_shutting_down(void)
673252bd125SHyeongseok Kim {
674252bd125SHyeongseok Kim return system_state == SYSTEM_HALT || system_state == SYSTEM_POWER_OFF
675252bd125SHyeongseok Kim || system_state == SYSTEM_RESTART;
676252bd125SHyeongseok Kim }
677252bd125SHyeongseok Kim
678252bd125SHyeongseok Kim /*
67903045cbaSSami Tolvanen * End one "io" structure with a given error.
68003045cbaSSami Tolvanen */
verity_finish_io(struct dm_verity_io * io,blk_status_t status)6814e4cbee9SChristoph Hellwig static void verity_finish_io(struct dm_verity_io *io, blk_status_t status)
68203045cbaSSami Tolvanen {
68303045cbaSSami Tolvanen struct dm_verity *v = io->v;
68430187e1dSMike Snitzer struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
68503045cbaSSami Tolvanen
68603045cbaSSami Tolvanen bio->bi_end_io = io->orig_bi_end_io;
6874e4cbee9SChristoph Hellwig bio->bi_status = status;
68803045cbaSSami Tolvanen
689ba2cce82SMike Snitzer if (!static_branch_unlikely(&use_tasklet_enabled) || !io->in_tasklet)
690a739ff3fSSami Tolvanen verity_fec_finish_io(io);
691a739ff3fSSami Tolvanen
69203045cbaSSami Tolvanen bio_endio(bio);
69303045cbaSSami Tolvanen }
69403045cbaSSami Tolvanen
verity_work(struct work_struct * w)69503045cbaSSami Tolvanen static void verity_work(struct work_struct *w)
69603045cbaSSami Tolvanen {
69703045cbaSSami Tolvanen struct dm_verity_io *io = container_of(w, struct dm_verity_io, work);
69803045cbaSSami Tolvanen
6995721d4e5SNathan Huckleberry io->in_tasklet = false;
7005721d4e5SNathan Huckleberry
7014e4cbee9SChristoph Hellwig verity_finish_io(io, errno_to_blk_status(verity_verify_io(io)));
70203045cbaSSami Tolvanen }
70303045cbaSSami Tolvanen
verity_end_io(struct bio * bio)70403045cbaSSami Tolvanen static void verity_end_io(struct bio *bio)
70503045cbaSSami Tolvanen {
70603045cbaSSami Tolvanen struct dm_verity_io *io = bio->bi_private;
70703045cbaSSami Tolvanen
708252bd125SHyeongseok Kim if (bio->bi_status &&
7097ec76526SWu Bo (!verity_fec_is_enabled(io->v) ||
7107ec76526SWu Bo verity_is_system_shutting_down() ||
7117ec76526SWu Bo (bio->bi_opf & REQ_RAHEAD))) {
7124e4cbee9SChristoph Hellwig verity_finish_io(io, bio->bi_status);
71303045cbaSSami Tolvanen return;
71403045cbaSSami Tolvanen }
71503045cbaSSami Tolvanen
71603045cbaSSami Tolvanen INIT_WORK(&io->work, verity_work);
71703045cbaSSami Tolvanen queue_work(io->v->verify_wq, &io->work);
71803045cbaSSami Tolvanen }
71903045cbaSSami Tolvanen
72003045cbaSSami Tolvanen /*
72103045cbaSSami Tolvanen * Prefetch buffers for the specified io.
72203045cbaSSami Tolvanen * The root buffer is not prefetched, it is assumed that it will be cached
72303045cbaSSami Tolvanen * all the time.
72403045cbaSSami Tolvanen */
verity_prefetch_io(struct work_struct * work)72503045cbaSSami Tolvanen static void verity_prefetch_io(struct work_struct *work)
72603045cbaSSami Tolvanen {
72703045cbaSSami Tolvanen struct dm_verity_prefetch_work *pw =
72803045cbaSSami Tolvanen container_of(work, struct dm_verity_prefetch_work, work);
72903045cbaSSami Tolvanen struct dm_verity *v = pw->v;
73003045cbaSSami Tolvanen int i;
73103045cbaSSami Tolvanen
73203045cbaSSami Tolvanen for (i = v->levels - 2; i >= 0; i--) {
73303045cbaSSami Tolvanen sector_t hash_block_start;
73403045cbaSSami Tolvanen sector_t hash_block_end;
7350ef0b471SHeinz Mauelshagen
73603045cbaSSami Tolvanen verity_hash_at_level(v, pw->block, i, &hash_block_start, NULL);
73703045cbaSSami Tolvanen verity_hash_at_level(v, pw->block + pw->n_blocks - 1, i, &hash_block_end, NULL);
7380ef0b471SHeinz Mauelshagen
73903045cbaSSami Tolvanen if (!i) {
74086a3238cSHeinz Mauelshagen unsigned int cluster = READ_ONCE(dm_verity_prefetch_cluster);
74103045cbaSSami Tolvanen
74203045cbaSSami Tolvanen cluster >>= v->data_dev_block_bits;
74303045cbaSSami Tolvanen if (unlikely(!cluster))
74403045cbaSSami Tolvanen goto no_prefetch_cluster;
74503045cbaSSami Tolvanen
74603045cbaSSami Tolvanen if (unlikely(cluster & (cluster - 1)))
74703045cbaSSami Tolvanen cluster = 1 << __fls(cluster);
74803045cbaSSami Tolvanen
74903045cbaSSami Tolvanen hash_block_start &= ~(sector_t)(cluster - 1);
75003045cbaSSami Tolvanen hash_block_end |= cluster - 1;
75103045cbaSSami Tolvanen if (unlikely(hash_block_end >= v->hash_blocks))
75203045cbaSSami Tolvanen hash_block_end = v->hash_blocks - 1;
75303045cbaSSami Tolvanen }
75403045cbaSSami Tolvanen no_prefetch_cluster:
75503045cbaSSami Tolvanen dm_bufio_prefetch(v->bufio, hash_block_start,
75603045cbaSSami Tolvanen hash_block_end - hash_block_start + 1);
75703045cbaSSami Tolvanen }
75803045cbaSSami Tolvanen
75903045cbaSSami Tolvanen kfree(pw);
76003045cbaSSami Tolvanen }
76103045cbaSSami Tolvanen
verity_submit_prefetch(struct dm_verity * v,struct dm_verity_io * io)76203045cbaSSami Tolvanen static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io)
76303045cbaSSami Tolvanen {
7640a531c5aSxianrong.zhou sector_t block = io->block;
7650a531c5aSxianrong.zhou unsigned int n_blocks = io->n_blocks;
76603045cbaSSami Tolvanen struct dm_verity_prefetch_work *pw;
76703045cbaSSami Tolvanen
7680a531c5aSxianrong.zhou if (v->validated_blocks) {
7690a531c5aSxianrong.zhou while (n_blocks && test_bit(block, v->validated_blocks)) {
7700a531c5aSxianrong.zhou block++;
7710a531c5aSxianrong.zhou n_blocks--;
7720a531c5aSxianrong.zhou }
7730a531c5aSxianrong.zhou while (n_blocks && test_bit(block + n_blocks - 1,
7740a531c5aSxianrong.zhou v->validated_blocks))
7750a531c5aSxianrong.zhou n_blocks--;
7760a531c5aSxianrong.zhou if (!n_blocks)
7770a531c5aSxianrong.zhou return;
7780a531c5aSxianrong.zhou }
7790a531c5aSxianrong.zhou
78003045cbaSSami Tolvanen pw = kmalloc(sizeof(struct dm_verity_prefetch_work),
78103045cbaSSami Tolvanen GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
78203045cbaSSami Tolvanen
78303045cbaSSami Tolvanen if (!pw)
78403045cbaSSami Tolvanen return;
78503045cbaSSami Tolvanen
78603045cbaSSami Tolvanen INIT_WORK(&pw->work, verity_prefetch_io);
78703045cbaSSami Tolvanen pw->v = v;
7880a531c5aSxianrong.zhou pw->block = block;
7890a531c5aSxianrong.zhou pw->n_blocks = n_blocks;
79003045cbaSSami Tolvanen queue_work(v->verify_wq, &pw->work);
79103045cbaSSami Tolvanen }
79203045cbaSSami Tolvanen
79303045cbaSSami Tolvanen /*
79403045cbaSSami Tolvanen * Bio map function. It allocates dm_verity_io structure and bio vector and
79503045cbaSSami Tolvanen * fills them. Then it issues prefetches and the I/O.
79603045cbaSSami Tolvanen */
verity_map(struct dm_target * ti,struct bio * bio)79703045cbaSSami Tolvanen static int verity_map(struct dm_target *ti, struct bio *bio)
79803045cbaSSami Tolvanen {
79903045cbaSSami Tolvanen struct dm_verity *v = ti->private;
80003045cbaSSami Tolvanen struct dm_verity_io *io;
80103045cbaSSami Tolvanen
80274d46992SChristoph Hellwig bio_set_dev(bio, v->data_dev->bdev);
80303045cbaSSami Tolvanen bio->bi_iter.bi_sector = verity_map_sector(v, bio->bi_iter.bi_sector);
80403045cbaSSami Tolvanen
80586a3238cSHeinz Mauelshagen if (((unsigned int)bio->bi_iter.bi_sector | bio_sectors(bio)) &
80603045cbaSSami Tolvanen ((1 << (v->data_dev_block_bits - SECTOR_SHIFT)) - 1)) {
80703045cbaSSami Tolvanen DMERR_LIMIT("unaligned io");
808846785e6SChristoph Hellwig return DM_MAPIO_KILL;
80903045cbaSSami Tolvanen }
81003045cbaSSami Tolvanen
81103045cbaSSami Tolvanen if (bio_end_sector(bio) >>
81203045cbaSSami Tolvanen (v->data_dev_block_bits - SECTOR_SHIFT) > v->data_blocks) {
81303045cbaSSami Tolvanen DMERR_LIMIT("io out of range");
814846785e6SChristoph Hellwig return DM_MAPIO_KILL;
81503045cbaSSami Tolvanen }
81603045cbaSSami Tolvanen
81703045cbaSSami Tolvanen if (bio_data_dir(bio) == WRITE)
818846785e6SChristoph Hellwig return DM_MAPIO_KILL;
81903045cbaSSami Tolvanen
82030187e1dSMike Snitzer io = dm_per_bio_data(bio, ti->per_io_data_size);
82103045cbaSSami Tolvanen io->v = v;
82203045cbaSSami Tolvanen io->orig_bi_end_io = bio->bi_end_io;
82303045cbaSSami Tolvanen io->block = bio->bi_iter.bi_sector >> (v->data_dev_block_bits - SECTOR_SHIFT);
82403045cbaSSami Tolvanen io->n_blocks = bio->bi_iter.bi_size >> v->data_dev_block_bits;
82503045cbaSSami Tolvanen
82603045cbaSSami Tolvanen bio->bi_end_io = verity_end_io;
82703045cbaSSami Tolvanen bio->bi_private = io;
82803045cbaSSami Tolvanen io->iter = bio->bi_iter;
82903045cbaSSami Tolvanen
830590df5e4SWu Bo verity_fec_init_io(io);
831590df5e4SWu Bo
83203045cbaSSami Tolvanen verity_submit_prefetch(v, io);
83303045cbaSSami Tolvanen
834ed00aabdSChristoph Hellwig submit_bio_noacct(bio);
83503045cbaSSami Tolvanen
83603045cbaSSami Tolvanen return DM_MAPIO_SUBMITTED;
83703045cbaSSami Tolvanen }
83803045cbaSSami Tolvanen
83903045cbaSSami Tolvanen /*
84003045cbaSSami Tolvanen * Status: V (valid) or C (corruption found)
84103045cbaSSami Tolvanen */
verity_status(struct dm_target * ti,status_type_t type,unsigned int status_flags,char * result,unsigned int maxlen)84203045cbaSSami Tolvanen static void verity_status(struct dm_target *ti, status_type_t type,
84386a3238cSHeinz Mauelshagen unsigned int status_flags, char *result, unsigned int maxlen)
84403045cbaSSami Tolvanen {
84503045cbaSSami Tolvanen struct dm_verity *v = ti->private;
84686a3238cSHeinz Mauelshagen unsigned int args = 0;
84786a3238cSHeinz Mauelshagen unsigned int sz = 0;
84886a3238cSHeinz Mauelshagen unsigned int x;
84903045cbaSSami Tolvanen
85003045cbaSSami Tolvanen switch (type) {
85103045cbaSSami Tolvanen case STATUSTYPE_INFO:
85203045cbaSSami Tolvanen DMEMIT("%c", v->hash_failed ? 'C' : 'V');
85303045cbaSSami Tolvanen break;
85403045cbaSSami Tolvanen case STATUSTYPE_TABLE:
85503045cbaSSami Tolvanen DMEMIT("%u %s %s %u %u %llu %llu %s ",
85603045cbaSSami Tolvanen v->version,
85703045cbaSSami Tolvanen v->data_dev->name,
85803045cbaSSami Tolvanen v->hash_dev->name,
85903045cbaSSami Tolvanen 1 << v->data_dev_block_bits,
86003045cbaSSami Tolvanen 1 << v->hash_dev_block_bits,
86103045cbaSSami Tolvanen (unsigned long long)v->data_blocks,
86203045cbaSSami Tolvanen (unsigned long long)v->hash_start,
86303045cbaSSami Tolvanen v->alg_name
86403045cbaSSami Tolvanen );
86503045cbaSSami Tolvanen for (x = 0; x < v->digest_size; x++)
86603045cbaSSami Tolvanen DMEMIT("%02x", v->root_digest[x]);
86703045cbaSSami Tolvanen DMEMIT(" ");
86803045cbaSSami Tolvanen if (!v->salt_size)
86903045cbaSSami Tolvanen DMEMIT("-");
87003045cbaSSami Tolvanen else
87103045cbaSSami Tolvanen for (x = 0; x < v->salt_size; x++)
87203045cbaSSami Tolvanen DMEMIT("%02x", v->salt[x]);
873a739ff3fSSami Tolvanen if (v->mode != DM_VERITY_MODE_EIO)
874a739ff3fSSami Tolvanen args++;
875a739ff3fSSami Tolvanen if (verity_fec_is_enabled(v))
876a739ff3fSSami Tolvanen args += DM_VERITY_OPTS_FEC;
8770cc37c2dSSami Tolvanen if (v->zero_digest)
8780cc37c2dSSami Tolvanen args++;
879843f38d3SPatrik Torstensson if (v->validated_blocks)
880843f38d3SPatrik Torstensson args++;
8815721d4e5SNathan Huckleberry if (v->use_tasklet)
8825721d4e5SNathan Huckleberry args++;
88388cd3e6cSJaskaran Khurana if (v->signature_key_desc)
88488cd3e6cSJaskaran Khurana args += DM_VERITY_ROOT_HASH_VERIFICATION_OPTS;
885a739ff3fSSami Tolvanen if (!args)
886a739ff3fSSami Tolvanen return;
887a739ff3fSSami Tolvanen DMEMIT(" %u", args);
88803045cbaSSami Tolvanen if (v->mode != DM_VERITY_MODE_EIO) {
889a739ff3fSSami Tolvanen DMEMIT(" ");
89003045cbaSSami Tolvanen switch (v->mode) {
89103045cbaSSami Tolvanen case DM_VERITY_MODE_LOGGING:
89203045cbaSSami Tolvanen DMEMIT(DM_VERITY_OPT_LOGGING);
89303045cbaSSami Tolvanen break;
89403045cbaSSami Tolvanen case DM_VERITY_MODE_RESTART:
89503045cbaSSami Tolvanen DMEMIT(DM_VERITY_OPT_RESTART);
89603045cbaSSami Tolvanen break;
897e1fef0b0SJeongHyeon Lee case DM_VERITY_MODE_PANIC:
898e1fef0b0SJeongHyeon Lee DMEMIT(DM_VERITY_OPT_PANIC);
899e1fef0b0SJeongHyeon Lee break;
90003045cbaSSami Tolvanen default:
90103045cbaSSami Tolvanen BUG();
90203045cbaSSami Tolvanen }
90303045cbaSSami Tolvanen }
9040cc37c2dSSami Tolvanen if (v->zero_digest)
9050cc37c2dSSami Tolvanen DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
906843f38d3SPatrik Torstensson if (v->validated_blocks)
907843f38d3SPatrik Torstensson DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE);
9085721d4e5SNathan Huckleberry if (v->use_tasklet)
9095721d4e5SNathan Huckleberry DMEMIT(" " DM_VERITY_OPT_TASKLET_VERIFY);
910a739ff3fSSami Tolvanen sz = verity_fec_status_table(v, sz, result, maxlen);
91188cd3e6cSJaskaran Khurana if (v->signature_key_desc)
91288cd3e6cSJaskaran Khurana DMEMIT(" " DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY
91388cd3e6cSJaskaran Khurana " %s", v->signature_key_desc);
91403045cbaSSami Tolvanen break;
9158ec45662STushar Sugandhi
9168ec45662STushar Sugandhi case STATUSTYPE_IMA:
9178ec45662STushar Sugandhi DMEMIT_TARGET_NAME_VERSION(ti->type);
9188ec45662STushar Sugandhi DMEMIT(",hash_failed=%c", v->hash_failed ? 'C' : 'V');
9198ec45662STushar Sugandhi DMEMIT(",verity_version=%u", v->version);
9208ec45662STushar Sugandhi DMEMIT(",data_device_name=%s", v->data_dev->name);
9218ec45662STushar Sugandhi DMEMIT(",hash_device_name=%s", v->hash_dev->name);
9228ec45662STushar Sugandhi DMEMIT(",verity_algorithm=%s", v->alg_name);
9238ec45662STushar Sugandhi
9248ec45662STushar Sugandhi DMEMIT(",root_digest=");
9258ec45662STushar Sugandhi for (x = 0; x < v->digest_size; x++)
9268ec45662STushar Sugandhi DMEMIT("%02x", v->root_digest[x]);
9278ec45662STushar Sugandhi
9288ec45662STushar Sugandhi DMEMIT(",salt=");
9298ec45662STushar Sugandhi if (!v->salt_size)
9308ec45662STushar Sugandhi DMEMIT("-");
9318ec45662STushar Sugandhi else
9328ec45662STushar Sugandhi for (x = 0; x < v->salt_size; x++)
9338ec45662STushar Sugandhi DMEMIT("%02x", v->salt[x]);
9348ec45662STushar Sugandhi
9358ec45662STushar Sugandhi DMEMIT(",ignore_zero_blocks=%c", v->zero_digest ? 'y' : 'n');
9368ec45662STushar Sugandhi DMEMIT(",check_at_most_once=%c", v->validated_blocks ? 'y' : 'n');
93733ace4caSTushar Sugandhi if (v->signature_key_desc)
93833ace4caSTushar Sugandhi DMEMIT(",root_hash_sig_key_desc=%s", v->signature_key_desc);
9398ec45662STushar Sugandhi
9408ec45662STushar Sugandhi if (v->mode != DM_VERITY_MODE_EIO) {
9418ec45662STushar Sugandhi DMEMIT(",verity_mode=");
9428ec45662STushar Sugandhi switch (v->mode) {
9438ec45662STushar Sugandhi case DM_VERITY_MODE_LOGGING:
9448ec45662STushar Sugandhi DMEMIT(DM_VERITY_OPT_LOGGING);
9458ec45662STushar Sugandhi break;
9468ec45662STushar Sugandhi case DM_VERITY_MODE_RESTART:
9478ec45662STushar Sugandhi DMEMIT(DM_VERITY_OPT_RESTART);
9488ec45662STushar Sugandhi break;
9498ec45662STushar Sugandhi case DM_VERITY_MODE_PANIC:
9508ec45662STushar Sugandhi DMEMIT(DM_VERITY_OPT_PANIC);
9518ec45662STushar Sugandhi break;
9528ec45662STushar Sugandhi default:
9538ec45662STushar Sugandhi DMEMIT("invalid");
9548ec45662STushar Sugandhi }
9558ec45662STushar Sugandhi }
9568ec45662STushar Sugandhi DMEMIT(";");
9578ec45662STushar Sugandhi break;
95803045cbaSSami Tolvanen }
95903045cbaSSami Tolvanen }
96003045cbaSSami Tolvanen
verity_prepare_ioctl(struct dm_target * ti,struct block_device ** bdev)9615bd5e8d8SMike Snitzer static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
96203045cbaSSami Tolvanen {
96303045cbaSSami Tolvanen struct dm_verity *v = ti->private;
96403045cbaSSami Tolvanen
96503045cbaSSami Tolvanen *bdev = v->data_dev->bdev;
96603045cbaSSami Tolvanen
9676dcbb52cSChristoph Hellwig if (v->data_start || ti->len != bdev_nr_sectors(v->data_dev->bdev))
96803045cbaSSami Tolvanen return 1;
96903045cbaSSami Tolvanen return 0;
97003045cbaSSami Tolvanen }
97103045cbaSSami Tolvanen
verity_iterate_devices(struct dm_target * ti,iterate_devices_callout_fn fn,void * data)97203045cbaSSami Tolvanen static int verity_iterate_devices(struct dm_target *ti,
97303045cbaSSami Tolvanen iterate_devices_callout_fn fn, void *data)
97403045cbaSSami Tolvanen {
97503045cbaSSami Tolvanen struct dm_verity *v = ti->private;
97603045cbaSSami Tolvanen
97703045cbaSSami Tolvanen return fn(ti, v->data_dev, v->data_start, ti->len, data);
97803045cbaSSami Tolvanen }
97903045cbaSSami Tolvanen
verity_io_hints(struct dm_target * ti,struct queue_limits * limits)98003045cbaSSami Tolvanen static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
98103045cbaSSami Tolvanen {
98203045cbaSSami Tolvanen struct dm_verity *v = ti->private;
98303045cbaSSami Tolvanen
98403045cbaSSami Tolvanen if (limits->logical_block_size < 1 << v->data_dev_block_bits)
98503045cbaSSami Tolvanen limits->logical_block_size = 1 << v->data_dev_block_bits;
98603045cbaSSami Tolvanen
98703045cbaSSami Tolvanen if (limits->physical_block_size < 1 << v->data_dev_block_bits)
98803045cbaSSami Tolvanen limits->physical_block_size = 1 << v->data_dev_block_bits;
98903045cbaSSami Tolvanen
99003045cbaSSami Tolvanen blk_limits_io_min(limits, limits->logical_block_size);
99103045cbaSSami Tolvanen }
99203045cbaSSami Tolvanen
verity_dtr(struct dm_target * ti)99303045cbaSSami Tolvanen static void verity_dtr(struct dm_target *ti)
99403045cbaSSami Tolvanen {
99503045cbaSSami Tolvanen struct dm_verity *v = ti->private;
99603045cbaSSami Tolvanen
99703045cbaSSami Tolvanen if (v->verify_wq)
99803045cbaSSami Tolvanen destroy_workqueue(v->verify_wq);
99903045cbaSSami Tolvanen
1000e5cc2309SMikulas Patocka mempool_exit(&v->recheck_pool);
1001e5cc2309SMikulas Patocka if (v->io)
1002e5cc2309SMikulas Patocka dm_io_client_destroy(v->io);
1003e5cc2309SMikulas Patocka
100403045cbaSSami Tolvanen if (v->bufio)
100503045cbaSSami Tolvanen dm_bufio_client_destroy(v->bufio);
100603045cbaSSami Tolvanen
1007843f38d3SPatrik Torstensson kvfree(v->validated_blocks);
100803045cbaSSami Tolvanen kfree(v->salt);
100903045cbaSSami Tolvanen kfree(v->root_digest);
10100cc37c2dSSami Tolvanen kfree(v->zero_digest);
101103045cbaSSami Tolvanen
101203045cbaSSami Tolvanen if (v->tfm)
1013d1ac3ff0SGilad Ben-Yossef crypto_free_ahash(v->tfm);
101403045cbaSSami Tolvanen
101503045cbaSSami Tolvanen kfree(v->alg_name);
101603045cbaSSami Tolvanen
101703045cbaSSami Tolvanen if (v->hash_dev)
101803045cbaSSami Tolvanen dm_put_device(ti, v->hash_dev);
101903045cbaSSami Tolvanen
102003045cbaSSami Tolvanen if (v->data_dev)
102103045cbaSSami Tolvanen dm_put_device(ti, v->data_dev);
102203045cbaSSami Tolvanen
1023a739ff3fSSami Tolvanen verity_fec_dtr(v);
1024a739ff3fSSami Tolvanen
102588cd3e6cSJaskaran Khurana kfree(v->signature_key_desc);
102688cd3e6cSJaskaran Khurana
1027ba2cce82SMike Snitzer if (v->use_tasklet)
1028ba2cce82SMike Snitzer static_branch_dec(&use_tasklet_enabled);
1029ba2cce82SMike Snitzer
103003045cbaSSami Tolvanen kfree(v);
1031074c4466SMichael Weiß
1032074c4466SMichael Weiß dm_audit_log_dtr(DM_MSG_PREFIX, ti, 1);
103303045cbaSSami Tolvanen }
103403045cbaSSami Tolvanen
verity_alloc_most_once(struct dm_verity * v)1035843f38d3SPatrik Torstensson static int verity_alloc_most_once(struct dm_verity *v)
1036843f38d3SPatrik Torstensson {
1037843f38d3SPatrik Torstensson struct dm_target *ti = v->ti;
1038843f38d3SPatrik Torstensson
1039843f38d3SPatrik Torstensson /* the bitset can only handle INT_MAX blocks */
1040843f38d3SPatrik Torstensson if (v->data_blocks > INT_MAX) {
1041843f38d3SPatrik Torstensson ti->error = "device too large to use check_at_most_once";
1042843f38d3SPatrik Torstensson return -E2BIG;
1043843f38d3SPatrik Torstensson }
1044843f38d3SPatrik Torstensson
1045778e1cddSKees Cook v->validated_blocks = kvcalloc(BITS_TO_LONGS(v->data_blocks),
1046778e1cddSKees Cook sizeof(unsigned long),
1047778e1cddSKees Cook GFP_KERNEL);
1048843f38d3SPatrik Torstensson if (!v->validated_blocks) {
1049843f38d3SPatrik Torstensson ti->error = "failed to allocate bitset for check_at_most_once";
1050843f38d3SPatrik Torstensson return -ENOMEM;
1051843f38d3SPatrik Torstensson }
1052843f38d3SPatrik Torstensson
1053843f38d3SPatrik Torstensson return 0;
1054843f38d3SPatrik Torstensson }
1055843f38d3SPatrik Torstensson
verity_alloc_zero_digest(struct dm_verity * v)10560cc37c2dSSami Tolvanen static int verity_alloc_zero_digest(struct dm_verity *v)
10570cc37c2dSSami Tolvanen {
10580cc37c2dSSami Tolvanen int r = -ENOMEM;
1059d1ac3ff0SGilad Ben-Yossef struct ahash_request *req;
10600cc37c2dSSami Tolvanen u8 *zero_data;
10610cc37c2dSSami Tolvanen
10620cc37c2dSSami Tolvanen v->zero_digest = kmalloc(v->digest_size, GFP_KERNEL);
10630cc37c2dSSami Tolvanen
10640cc37c2dSSami Tolvanen if (!v->zero_digest)
10650cc37c2dSSami Tolvanen return r;
10660cc37c2dSSami Tolvanen
1067d1ac3ff0SGilad Ben-Yossef req = kmalloc(v->ahash_reqsize, GFP_KERNEL);
10680cc37c2dSSami Tolvanen
1069d1ac3ff0SGilad Ben-Yossef if (!req)
10700cc37c2dSSami Tolvanen return r; /* verity_dtr will free zero_digest */
10710cc37c2dSSami Tolvanen
10720cc37c2dSSami Tolvanen zero_data = kzalloc(1 << v->data_dev_block_bits, GFP_KERNEL);
10730cc37c2dSSami Tolvanen
10740cc37c2dSSami Tolvanen if (!zero_data)
10750cc37c2dSSami Tolvanen goto out;
10760cc37c2dSSami Tolvanen
1077d1ac3ff0SGilad Ben-Yossef r = verity_hash(v, req, zero_data, 1 << v->data_dev_block_bits,
107857985252SMikulas Patocka v->zero_digest, true);
10790cc37c2dSSami Tolvanen
10800cc37c2dSSami Tolvanen out:
1081d1ac3ff0SGilad Ben-Yossef kfree(req);
10820cc37c2dSSami Tolvanen kfree(zero_data);
10830cc37c2dSSami Tolvanen
10840cc37c2dSSami Tolvanen return r;
10850cc37c2dSSami Tolvanen }
10860cc37c2dSSami Tolvanen
verity_is_verity_mode(const char * arg_name)1087219a9b5eSJeongHyeon Lee static inline bool verity_is_verity_mode(const char *arg_name)
1088219a9b5eSJeongHyeon Lee {
1089219a9b5eSJeongHyeon Lee return (!strcasecmp(arg_name, DM_VERITY_OPT_LOGGING) ||
1090219a9b5eSJeongHyeon Lee !strcasecmp(arg_name, DM_VERITY_OPT_RESTART) ||
1091219a9b5eSJeongHyeon Lee !strcasecmp(arg_name, DM_VERITY_OPT_PANIC));
1092219a9b5eSJeongHyeon Lee }
1093219a9b5eSJeongHyeon Lee
verity_parse_verity_mode(struct dm_verity * v,const char * arg_name)1094219a9b5eSJeongHyeon Lee static int verity_parse_verity_mode(struct dm_verity *v, const char *arg_name)
1095219a9b5eSJeongHyeon Lee {
1096219a9b5eSJeongHyeon Lee if (v->mode)
1097219a9b5eSJeongHyeon Lee return -EINVAL;
1098219a9b5eSJeongHyeon Lee
1099219a9b5eSJeongHyeon Lee if (!strcasecmp(arg_name, DM_VERITY_OPT_LOGGING))
1100219a9b5eSJeongHyeon Lee v->mode = DM_VERITY_MODE_LOGGING;
1101219a9b5eSJeongHyeon Lee else if (!strcasecmp(arg_name, DM_VERITY_OPT_RESTART))
1102219a9b5eSJeongHyeon Lee v->mode = DM_VERITY_MODE_RESTART;
1103219a9b5eSJeongHyeon Lee else if (!strcasecmp(arg_name, DM_VERITY_OPT_PANIC))
1104219a9b5eSJeongHyeon Lee v->mode = DM_VERITY_MODE_PANIC;
1105219a9b5eSJeongHyeon Lee
1106219a9b5eSJeongHyeon Lee return 0;
1107219a9b5eSJeongHyeon Lee }
1108219a9b5eSJeongHyeon Lee
verity_parse_opt_args(struct dm_arg_set * as,struct dm_verity * v,struct dm_verity_sig_opts * verify_args,bool only_modifier_opts)110988cd3e6cSJaskaran Khurana static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
1110df326e7aSMike Snitzer struct dm_verity_sig_opts *verify_args,
1111df326e7aSMike Snitzer bool only_modifier_opts)
111203045cbaSSami Tolvanen {
1113f876df9fSMike Snitzer int r = 0;
111486a3238cSHeinz Mauelshagen unsigned int argc;
111503045cbaSSami Tolvanen struct dm_target *ti = v->ti;
111603045cbaSSami Tolvanen const char *arg_name;
111703045cbaSSami Tolvanen
11185916a22bSEric Biggers static const struct dm_arg _args[] = {
111903045cbaSSami Tolvanen {0, DM_VERITY_OPTS_MAX, "Invalid number of feature args"},
112003045cbaSSami Tolvanen };
112103045cbaSSami Tolvanen
112203045cbaSSami Tolvanen r = dm_read_arg_group(_args, as, &argc, &ti->error);
112303045cbaSSami Tolvanen if (r)
112403045cbaSSami Tolvanen return -EINVAL;
112503045cbaSSami Tolvanen
112603045cbaSSami Tolvanen if (!argc)
112703045cbaSSami Tolvanen return 0;
112803045cbaSSami Tolvanen
112903045cbaSSami Tolvanen do {
113003045cbaSSami Tolvanen arg_name = dm_shift_arg(as);
113103045cbaSSami Tolvanen argc--;
113203045cbaSSami Tolvanen
1133219a9b5eSJeongHyeon Lee if (verity_is_verity_mode(arg_name)) {
1134df326e7aSMike Snitzer if (only_modifier_opts)
1135df326e7aSMike Snitzer continue;
1136219a9b5eSJeongHyeon Lee r = verity_parse_verity_mode(v, arg_name);
1137219a9b5eSJeongHyeon Lee if (r) {
1138219a9b5eSJeongHyeon Lee ti->error = "Conflicting error handling parameters";
1139219a9b5eSJeongHyeon Lee return r;
1140219a9b5eSJeongHyeon Lee }
1141e1fef0b0SJeongHyeon Lee continue;
1142e1fef0b0SJeongHyeon Lee
11430cc37c2dSSami Tolvanen } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) {
1144df326e7aSMike Snitzer if (only_modifier_opts)
1145df326e7aSMike Snitzer continue;
11460cc37c2dSSami Tolvanen r = verity_alloc_zero_digest(v);
11470cc37c2dSSami Tolvanen if (r) {
11480cc37c2dSSami Tolvanen ti->error = "Cannot allocate zero digest";
11490cc37c2dSSami Tolvanen return r;
11500cc37c2dSSami Tolvanen }
11510cc37c2dSSami Tolvanen continue;
11520cc37c2dSSami Tolvanen
1153843f38d3SPatrik Torstensson } else if (!strcasecmp(arg_name, DM_VERITY_OPT_AT_MOST_ONCE)) {
1154df326e7aSMike Snitzer if (only_modifier_opts)
1155df326e7aSMike Snitzer continue;
1156843f38d3SPatrik Torstensson r = verity_alloc_most_once(v);
1157843f38d3SPatrik Torstensson if (r)
1158843f38d3SPatrik Torstensson return r;
1159843f38d3SPatrik Torstensson continue;
1160843f38d3SPatrik Torstensson
11615721d4e5SNathan Huckleberry } else if (!strcasecmp(arg_name, DM_VERITY_OPT_TASKLET_VERIFY)) {
11625721d4e5SNathan Huckleberry v->use_tasklet = true;
1163ba2cce82SMike Snitzer static_branch_inc(&use_tasklet_enabled);
11645721d4e5SNathan Huckleberry continue;
11655721d4e5SNathan Huckleberry
1166a739ff3fSSami Tolvanen } else if (verity_is_fec_opt_arg(arg_name)) {
1167df326e7aSMike Snitzer if (only_modifier_opts)
1168df326e7aSMike Snitzer continue;
1169a739ff3fSSami Tolvanen r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
1170a739ff3fSSami Tolvanen if (r)
1171a739ff3fSSami Tolvanen return r;
1172a739ff3fSSami Tolvanen continue;
11735721d4e5SNathan Huckleberry
117488cd3e6cSJaskaran Khurana } else if (verity_verify_is_sig_opt_arg(arg_name)) {
1175df326e7aSMike Snitzer if (only_modifier_opts)
1176df326e7aSMike Snitzer continue;
117788cd3e6cSJaskaran Khurana r = verity_verify_sig_parse_opt_args(as, v,
117888cd3e6cSJaskaran Khurana verify_args,
117988cd3e6cSJaskaran Khurana &argc, arg_name);
118088cd3e6cSJaskaran Khurana if (r)
118188cd3e6cSJaskaran Khurana return r;
118288cd3e6cSJaskaran Khurana continue;
1183f876df9fSMike Snitzer
1184f876df9fSMike Snitzer } else if (only_modifier_opts) {
1185f876df9fSMike Snitzer /*
1186f876df9fSMike Snitzer * Ignore unrecognized opt, could easily be an extra
1187f876df9fSMike Snitzer * argument to an option whose parsing was skipped.
1188f876df9fSMike Snitzer * Normal parsing (@only_modifier_opts=false) will
1189f876df9fSMike Snitzer * properly parse all options (and their extra args).
1190f876df9fSMike Snitzer */
1191f876df9fSMike Snitzer continue;
119203045cbaSSami Tolvanen }
119303045cbaSSami Tolvanen
1194f876df9fSMike Snitzer DMERR("Unrecognized verity feature request: %s", arg_name);
119503045cbaSSami Tolvanen ti->error = "Unrecognized verity feature request";
119603045cbaSSami Tolvanen return -EINVAL;
119703045cbaSSami Tolvanen } while (argc && !r);
119803045cbaSSami Tolvanen
119903045cbaSSami Tolvanen return r;
120003045cbaSSami Tolvanen }
120103045cbaSSami Tolvanen
120203045cbaSSami Tolvanen /*
120303045cbaSSami Tolvanen * Target parameters:
120403045cbaSSami Tolvanen * <version> The current format is version 1.
120503045cbaSSami Tolvanen * Vsn 0 is compatible with original Chromium OS releases.
120603045cbaSSami Tolvanen * <data device>
120703045cbaSSami Tolvanen * <hash device>
120803045cbaSSami Tolvanen * <data block size>
120903045cbaSSami Tolvanen * <hash block size>
121003045cbaSSami Tolvanen * <the number of data blocks>
121103045cbaSSami Tolvanen * <hash start block>
121203045cbaSSami Tolvanen * <algorithm>
121303045cbaSSami Tolvanen * <digest>
121403045cbaSSami Tolvanen * <salt> Hex string or "-" if no salt.
121503045cbaSSami Tolvanen */
verity_ctr(struct dm_target * ti,unsigned int argc,char ** argv)121686a3238cSHeinz Mauelshagen static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
121703045cbaSSami Tolvanen {
121803045cbaSSami Tolvanen struct dm_verity *v;
121988cd3e6cSJaskaran Khurana struct dm_verity_sig_opts verify_args = {0};
122003045cbaSSami Tolvanen struct dm_arg_set as;
122103045cbaSSami Tolvanen unsigned int num;
122203045cbaSSami Tolvanen unsigned long long num_ll;
122303045cbaSSami Tolvanen int r;
122403045cbaSSami Tolvanen int i;
122503045cbaSSami Tolvanen sector_t hash_position;
122603045cbaSSami Tolvanen char dummy;
122788cd3e6cSJaskaran Khurana char *root_hash_digest_to_validate;
122803045cbaSSami Tolvanen
122903045cbaSSami Tolvanen v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
123003045cbaSSami Tolvanen if (!v) {
123103045cbaSSami Tolvanen ti->error = "Cannot allocate verity structure";
123203045cbaSSami Tolvanen return -ENOMEM;
123303045cbaSSami Tolvanen }
123403045cbaSSami Tolvanen ti->private = v;
123503045cbaSSami Tolvanen v->ti = ti;
123603045cbaSSami Tolvanen
1237a739ff3fSSami Tolvanen r = verity_fec_ctr_alloc(v);
1238a739ff3fSSami Tolvanen if (r)
1239a739ff3fSSami Tolvanen goto bad;
1240a739ff3fSSami Tolvanen
124105bdb996SChristoph Hellwig if ((dm_table_get_mode(ti->table) & ~BLK_OPEN_READ)) {
124203045cbaSSami Tolvanen ti->error = "Device must be readonly";
124303045cbaSSami Tolvanen r = -EINVAL;
124403045cbaSSami Tolvanen goto bad;
124503045cbaSSami Tolvanen }
124603045cbaSSami Tolvanen
124703045cbaSSami Tolvanen if (argc < 10) {
124803045cbaSSami Tolvanen ti->error = "Not enough arguments";
124903045cbaSSami Tolvanen r = -EINVAL;
125003045cbaSSami Tolvanen goto bad;
125103045cbaSSami Tolvanen }
125203045cbaSSami Tolvanen
1253df326e7aSMike Snitzer /* Parse optional parameters that modify primary args */
1254df326e7aSMike Snitzer if (argc > 10) {
1255df326e7aSMike Snitzer as.argc = argc - 10;
1256df326e7aSMike Snitzer as.argv = argv + 10;
1257df326e7aSMike Snitzer r = verity_parse_opt_args(&as, v, &verify_args, true);
1258df326e7aSMike Snitzer if (r < 0)
1259df326e7aSMike Snitzer goto bad;
1260df326e7aSMike Snitzer }
1261df326e7aSMike Snitzer
126203045cbaSSami Tolvanen if (sscanf(argv[0], "%u%c", &num, &dummy) != 1 ||
126303045cbaSSami Tolvanen num > 1) {
126403045cbaSSami Tolvanen ti->error = "Invalid version";
126503045cbaSSami Tolvanen r = -EINVAL;
126603045cbaSSami Tolvanen goto bad;
126703045cbaSSami Tolvanen }
126803045cbaSSami Tolvanen v->version = num;
126903045cbaSSami Tolvanen
127005bdb996SChristoph Hellwig r = dm_get_device(ti, argv[1], BLK_OPEN_READ, &v->data_dev);
127103045cbaSSami Tolvanen if (r) {
127203045cbaSSami Tolvanen ti->error = "Data device lookup failed";
127303045cbaSSami Tolvanen goto bad;
127403045cbaSSami Tolvanen }
127503045cbaSSami Tolvanen
127605bdb996SChristoph Hellwig r = dm_get_device(ti, argv[2], BLK_OPEN_READ, &v->hash_dev);
127703045cbaSSami Tolvanen if (r) {
127821ffe552SEric Biggers ti->error = "Hash device lookup failed";
127903045cbaSSami Tolvanen goto bad;
128003045cbaSSami Tolvanen }
128103045cbaSSami Tolvanen
128203045cbaSSami Tolvanen if (sscanf(argv[3], "%u%c", &num, &dummy) != 1 ||
128303045cbaSSami Tolvanen !num || (num & (num - 1)) ||
128403045cbaSSami Tolvanen num < bdev_logical_block_size(v->data_dev->bdev) ||
128503045cbaSSami Tolvanen num > PAGE_SIZE) {
128603045cbaSSami Tolvanen ti->error = "Invalid data device block size";
128703045cbaSSami Tolvanen r = -EINVAL;
128803045cbaSSami Tolvanen goto bad;
128903045cbaSSami Tolvanen }
129003045cbaSSami Tolvanen v->data_dev_block_bits = __ffs(num);
129103045cbaSSami Tolvanen
129203045cbaSSami Tolvanen if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
129303045cbaSSami Tolvanen !num || (num & (num - 1)) ||
129403045cbaSSami Tolvanen num < bdev_logical_block_size(v->hash_dev->bdev) ||
129503045cbaSSami Tolvanen num > INT_MAX) {
129603045cbaSSami Tolvanen ti->error = "Invalid hash device block size";
129703045cbaSSami Tolvanen r = -EINVAL;
129803045cbaSSami Tolvanen goto bad;
129903045cbaSSami Tolvanen }
130003045cbaSSami Tolvanen v->hash_dev_block_bits = __ffs(num);
130103045cbaSSami Tolvanen
130203045cbaSSami Tolvanen if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
130303045cbaSSami Tolvanen (sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
130403045cbaSSami Tolvanen >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll) {
130503045cbaSSami Tolvanen ti->error = "Invalid data blocks";
130603045cbaSSami Tolvanen r = -EINVAL;
130703045cbaSSami Tolvanen goto bad;
130803045cbaSSami Tolvanen }
130903045cbaSSami Tolvanen v->data_blocks = num_ll;
131003045cbaSSami Tolvanen
131103045cbaSSami Tolvanen if (ti->len > (v->data_blocks << (v->data_dev_block_bits - SECTOR_SHIFT))) {
131203045cbaSSami Tolvanen ti->error = "Data device is too small";
131303045cbaSSami Tolvanen r = -EINVAL;
131403045cbaSSami Tolvanen goto bad;
131503045cbaSSami Tolvanen }
131603045cbaSSami Tolvanen
131703045cbaSSami Tolvanen if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
131803045cbaSSami Tolvanen (sector_t)(num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT))
131903045cbaSSami Tolvanen >> (v->hash_dev_block_bits - SECTOR_SHIFT) != num_ll) {
132003045cbaSSami Tolvanen ti->error = "Invalid hash start";
132103045cbaSSami Tolvanen r = -EINVAL;
132203045cbaSSami Tolvanen goto bad;
132303045cbaSSami Tolvanen }
132403045cbaSSami Tolvanen v->hash_start = num_ll;
132503045cbaSSami Tolvanen
132603045cbaSSami Tolvanen v->alg_name = kstrdup(argv[7], GFP_KERNEL);
132703045cbaSSami Tolvanen if (!v->alg_name) {
132803045cbaSSami Tolvanen ti->error = "Cannot allocate algorithm name";
132903045cbaSSami Tolvanen r = -ENOMEM;
133003045cbaSSami Tolvanen goto bad;
133103045cbaSSami Tolvanen }
133203045cbaSSami Tolvanen
1333df326e7aSMike Snitzer v->tfm = crypto_alloc_ahash(v->alg_name, 0,
1334df326e7aSMike Snitzer v->use_tasklet ? CRYPTO_ALG_ASYNC : 0);
133503045cbaSSami Tolvanen if (IS_ERR(v->tfm)) {
133603045cbaSSami Tolvanen ti->error = "Cannot initialize hash function";
133703045cbaSSami Tolvanen r = PTR_ERR(v->tfm);
133803045cbaSSami Tolvanen v->tfm = NULL;
133903045cbaSSami Tolvanen goto bad;
134003045cbaSSami Tolvanen }
1341bbf6a566SEric Biggers
1342bbf6a566SEric Biggers /*
1343bbf6a566SEric Biggers * dm-verity performance can vary greatly depending on which hash
1344bbf6a566SEric Biggers * algorithm implementation is used. Help people debug performance
1345bbf6a566SEric Biggers * problems by logging the ->cra_driver_name.
1346bbf6a566SEric Biggers */
1347bbf6a566SEric Biggers DMINFO("%s using implementation \"%s\"", v->alg_name,
1348bbf6a566SEric Biggers crypto_hash_alg_common(v->tfm)->base.cra_driver_name);
1349bbf6a566SEric Biggers
1350d1ac3ff0SGilad Ben-Yossef v->digest_size = crypto_ahash_digestsize(v->tfm);
135103045cbaSSami Tolvanen if ((1 << v->hash_dev_block_bits) < v->digest_size * 2) {
135203045cbaSSami Tolvanen ti->error = "Digest size too big";
135303045cbaSSami Tolvanen r = -EINVAL;
135403045cbaSSami Tolvanen goto bad;
135503045cbaSSami Tolvanen }
1356d1ac3ff0SGilad Ben-Yossef v->ahash_reqsize = sizeof(struct ahash_request) +
1357d1ac3ff0SGilad Ben-Yossef crypto_ahash_reqsize(v->tfm);
135803045cbaSSami Tolvanen
135903045cbaSSami Tolvanen v->root_digest = kmalloc(v->digest_size, GFP_KERNEL);
136003045cbaSSami Tolvanen if (!v->root_digest) {
136103045cbaSSami Tolvanen ti->error = "Cannot allocate root digest";
136203045cbaSSami Tolvanen r = -ENOMEM;
136303045cbaSSami Tolvanen goto bad;
136403045cbaSSami Tolvanen }
136503045cbaSSami Tolvanen if (strlen(argv[8]) != v->digest_size * 2 ||
136603045cbaSSami Tolvanen hex2bin(v->root_digest, argv[8], v->digest_size)) {
136703045cbaSSami Tolvanen ti->error = "Invalid root digest";
136803045cbaSSami Tolvanen r = -EINVAL;
136903045cbaSSami Tolvanen goto bad;
137003045cbaSSami Tolvanen }
137188cd3e6cSJaskaran Khurana root_hash_digest_to_validate = argv[8];
137203045cbaSSami Tolvanen
137303045cbaSSami Tolvanen if (strcmp(argv[9], "-")) {
137403045cbaSSami Tolvanen v->salt_size = strlen(argv[9]) / 2;
137503045cbaSSami Tolvanen v->salt = kmalloc(v->salt_size, GFP_KERNEL);
137603045cbaSSami Tolvanen if (!v->salt) {
137703045cbaSSami Tolvanen ti->error = "Cannot allocate salt";
137803045cbaSSami Tolvanen r = -ENOMEM;
137903045cbaSSami Tolvanen goto bad;
138003045cbaSSami Tolvanen }
138103045cbaSSami Tolvanen if (strlen(argv[9]) != v->salt_size * 2 ||
138203045cbaSSami Tolvanen hex2bin(v->salt, argv[9], v->salt_size)) {
138303045cbaSSami Tolvanen ti->error = "Invalid salt";
138403045cbaSSami Tolvanen r = -EINVAL;
138503045cbaSSami Tolvanen goto bad;
138603045cbaSSami Tolvanen }
138703045cbaSSami Tolvanen }
138803045cbaSSami Tolvanen
138903045cbaSSami Tolvanen argv += 10;
139003045cbaSSami Tolvanen argc -= 10;
139103045cbaSSami Tolvanen
139203045cbaSSami Tolvanen /* Optional parameters */
139303045cbaSSami Tolvanen if (argc) {
139403045cbaSSami Tolvanen as.argc = argc;
139503045cbaSSami Tolvanen as.argv = argv;
1396df326e7aSMike Snitzer r = verity_parse_opt_args(&as, v, &verify_args, false);
139703045cbaSSami Tolvanen if (r < 0)
139803045cbaSSami Tolvanen goto bad;
139903045cbaSSami Tolvanen }
140003045cbaSSami Tolvanen
140188cd3e6cSJaskaran Khurana /* Root hash signature is a optional parameter*/
140288cd3e6cSJaskaran Khurana r = verity_verify_root_hash(root_hash_digest_to_validate,
140388cd3e6cSJaskaran Khurana strlen(root_hash_digest_to_validate),
140488cd3e6cSJaskaran Khurana verify_args.sig,
140588cd3e6cSJaskaran Khurana verify_args.sig_size);
140688cd3e6cSJaskaran Khurana if (r < 0) {
140788cd3e6cSJaskaran Khurana ti->error = "Root hash verification failed";
140888cd3e6cSJaskaran Khurana goto bad;
140988cd3e6cSJaskaran Khurana }
141003045cbaSSami Tolvanen v->hash_per_block_bits =
141103045cbaSSami Tolvanen __fls((1 << v->hash_dev_block_bits) / v->digest_size);
141203045cbaSSami Tolvanen
141303045cbaSSami Tolvanen v->levels = 0;
141403045cbaSSami Tolvanen if (v->data_blocks)
141503045cbaSSami Tolvanen while (v->hash_per_block_bits * v->levels < 64 &&
141603045cbaSSami Tolvanen (unsigned long long)(v->data_blocks - 1) >>
141703045cbaSSami Tolvanen (v->hash_per_block_bits * v->levels))
141803045cbaSSami Tolvanen v->levels++;
141903045cbaSSami Tolvanen
142003045cbaSSami Tolvanen if (v->levels > DM_VERITY_MAX_LEVELS) {
142103045cbaSSami Tolvanen ti->error = "Too many tree levels";
142203045cbaSSami Tolvanen r = -E2BIG;
142303045cbaSSami Tolvanen goto bad;
142403045cbaSSami Tolvanen }
142503045cbaSSami Tolvanen
142603045cbaSSami Tolvanen hash_position = v->hash_start;
142703045cbaSSami Tolvanen for (i = v->levels - 1; i >= 0; i--) {
142803045cbaSSami Tolvanen sector_t s;
14290ef0b471SHeinz Mauelshagen
143003045cbaSSami Tolvanen v->hash_level_block[i] = hash_position;
143103045cbaSSami Tolvanen s = (v->data_blocks + ((sector_t)1 << ((i + 1) * v->hash_per_block_bits)) - 1)
143203045cbaSSami Tolvanen >> ((i + 1) * v->hash_per_block_bits);
143303045cbaSSami Tolvanen if (hash_position + s < hash_position) {
143403045cbaSSami Tolvanen ti->error = "Hash device offset overflow";
143503045cbaSSami Tolvanen r = -E2BIG;
143603045cbaSSami Tolvanen goto bad;
143703045cbaSSami Tolvanen }
143803045cbaSSami Tolvanen hash_position += s;
143903045cbaSSami Tolvanen }
144003045cbaSSami Tolvanen v->hash_blocks = hash_position;
144103045cbaSSami Tolvanen
1442e5cc2309SMikulas Patocka r = mempool_init_page_pool(&v->recheck_pool, 1, 0);
1443e5cc2309SMikulas Patocka if (unlikely(r)) {
1444e5cc2309SMikulas Patocka ti->error = "Cannot allocate mempool";
1445e5cc2309SMikulas Patocka goto bad;
1446e5cc2309SMikulas Patocka }
1447e5cc2309SMikulas Patocka
1448e5cc2309SMikulas Patocka v->io = dm_io_client_create();
1449e5cc2309SMikulas Patocka if (IS_ERR(v->io)) {
1450e5cc2309SMikulas Patocka r = PTR_ERR(v->io);
1451e5cc2309SMikulas Patocka v->io = NULL;
1452e5cc2309SMikulas Patocka ti->error = "Cannot allocate dm io";
1453e5cc2309SMikulas Patocka goto bad;
1454e5cc2309SMikulas Patocka }
1455e5cc2309SMikulas Patocka
145603045cbaSSami Tolvanen v->bufio = dm_bufio_client_create(v->hash_dev->bdev,
145703045cbaSSami Tolvanen 1 << v->hash_dev_block_bits, 1, sizeof(struct buffer_aux),
14585721d4e5SNathan Huckleberry dm_bufio_alloc_callback, NULL,
14595721d4e5SNathan Huckleberry v->use_tasklet ? DM_BUFIO_CLIENT_NO_SLEEP : 0);
146003045cbaSSami Tolvanen if (IS_ERR(v->bufio)) {
146103045cbaSSami Tolvanen ti->error = "Cannot initialize dm-bufio";
146203045cbaSSami Tolvanen r = PTR_ERR(v->bufio);
146303045cbaSSami Tolvanen v->bufio = NULL;
146403045cbaSSami Tolvanen goto bad;
146503045cbaSSami Tolvanen }
146603045cbaSSami Tolvanen
146703045cbaSSami Tolvanen if (dm_bufio_get_device_size(v->bufio) < v->hash_blocks) {
146803045cbaSSami Tolvanen ti->error = "Hash device is too small";
146903045cbaSSami Tolvanen r = -E2BIG;
147003045cbaSSami Tolvanen goto bad;
147103045cbaSSami Tolvanen }
147203045cbaSSami Tolvanen
147312907efdSMike Snitzer /*
1474afd41fffSNathan Huckleberry * Using WQ_HIGHPRI improves throughput and completion latency by
1475afd41fffSNathan Huckleberry * reducing wait times when reading from a dm-verity device.
1476afd41fffSNathan Huckleberry *
1477afd41fffSNathan Huckleberry * Also as required for the "try_verify_in_tasklet" feature: WQ_HIGHPRI
1478afd41fffSNathan Huckleberry * allows verify_wq to preempt softirq since verification in tasklet
1479afd41fffSNathan Huckleberry * will fall-back to using it for error handling (or if the bufio cache
1480afd41fffSNathan Huckleberry * doesn't have required hashes).
148112907efdSMike Snitzer */
1482c25da5b7SNathan Huckleberry v->verify_wq = alloc_workqueue("kverityd", WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
148303045cbaSSami Tolvanen if (!v->verify_wq) {
148403045cbaSSami Tolvanen ti->error = "Cannot allocate workqueue";
148503045cbaSSami Tolvanen r = -ENOMEM;
148603045cbaSSami Tolvanen goto bad;
148703045cbaSSami Tolvanen }
148803045cbaSSami Tolvanen
148930187e1dSMike Snitzer ti->per_io_data_size = sizeof(struct dm_verity_io) +
1490d1ac3ff0SGilad Ben-Yossef v->ahash_reqsize + v->digest_size * 2;
1491a739ff3fSSami Tolvanen
1492a739ff3fSSami Tolvanen r = verity_fec_ctr(v);
1493a739ff3fSSami Tolvanen if (r)
1494a739ff3fSSami Tolvanen goto bad;
1495a739ff3fSSami Tolvanen
149630187e1dSMike Snitzer ti->per_io_data_size = roundup(ti->per_io_data_size,
1497a739ff3fSSami Tolvanen __alignof__(struct dm_verity_io));
1498a739ff3fSSami Tolvanen
149988cd3e6cSJaskaran Khurana verity_verify_sig_opts_cleanup(&verify_args);
150088cd3e6cSJaskaran Khurana
1501074c4466SMichael Weiß dm_audit_log_ctr(DM_MSG_PREFIX, ti, 1);
1502074c4466SMichael Weiß
150303045cbaSSami Tolvanen return 0;
150403045cbaSSami Tolvanen
150503045cbaSSami Tolvanen bad:
150688cd3e6cSJaskaran Khurana
150788cd3e6cSJaskaran Khurana verity_verify_sig_opts_cleanup(&verify_args);
1508074c4466SMichael Weiß dm_audit_log_ctr(DM_MSG_PREFIX, ti, 0);
150903045cbaSSami Tolvanen verity_dtr(ti);
151003045cbaSSami Tolvanen
151103045cbaSSami Tolvanen return r;
151203045cbaSSami Tolvanen }
151303045cbaSSami Tolvanen
1514b6c1c574SMatthias Kaehlcke /*
1515916ef623SMatthias Kaehlcke * Get the verity mode (error behavior) of a verity target.
1516916ef623SMatthias Kaehlcke *
1517916ef623SMatthias Kaehlcke * Returns the verity mode of the target, or -EINVAL if 'ti' is not a verity
1518916ef623SMatthias Kaehlcke * target.
1519916ef623SMatthias Kaehlcke */
dm_verity_get_mode(struct dm_target * ti)1520916ef623SMatthias Kaehlcke int dm_verity_get_mode(struct dm_target *ti)
1521916ef623SMatthias Kaehlcke {
1522916ef623SMatthias Kaehlcke struct dm_verity *v = ti->private;
1523916ef623SMatthias Kaehlcke
1524916ef623SMatthias Kaehlcke if (!dm_is_verity_target(ti))
1525916ef623SMatthias Kaehlcke return -EINVAL;
1526916ef623SMatthias Kaehlcke
1527916ef623SMatthias Kaehlcke return v->mode;
1528916ef623SMatthias Kaehlcke }
1529916ef623SMatthias Kaehlcke
1530916ef623SMatthias Kaehlcke /*
1531b6c1c574SMatthias Kaehlcke * Get the root digest of a verity target.
1532b6c1c574SMatthias Kaehlcke *
1533b6c1c574SMatthias Kaehlcke * Returns a copy of the root digest, the caller is responsible for
1534b6c1c574SMatthias Kaehlcke * freeing the memory of the digest.
1535b6c1c574SMatthias Kaehlcke */
dm_verity_get_root_digest(struct dm_target * ti,u8 ** root_digest,unsigned int * digest_size)1536b6c1c574SMatthias Kaehlcke int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest, unsigned int *digest_size)
1537b6c1c574SMatthias Kaehlcke {
1538b6c1c574SMatthias Kaehlcke struct dm_verity *v = ti->private;
1539b6c1c574SMatthias Kaehlcke
1540b6c1c574SMatthias Kaehlcke if (!dm_is_verity_target(ti))
1541b6c1c574SMatthias Kaehlcke return -EINVAL;
1542b6c1c574SMatthias Kaehlcke
1543b6c1c574SMatthias Kaehlcke *root_digest = kmemdup(v->root_digest, v->digest_size, GFP_KERNEL);
1544b6c1c574SMatthias Kaehlcke if (*root_digest == NULL)
1545b6c1c574SMatthias Kaehlcke return -ENOMEM;
1546b6c1c574SMatthias Kaehlcke
1547b6c1c574SMatthias Kaehlcke *digest_size = v->digest_size;
1548b6c1c574SMatthias Kaehlcke
1549b6c1c574SMatthias Kaehlcke return 0;
1550b6c1c574SMatthias Kaehlcke }
1551b6c1c574SMatthias Kaehlcke
155203045cbaSSami Tolvanen static struct target_type verity_target = {
155303045cbaSSami Tolvanen .name = "verity",
15544caae584SSarthak Kukreti .features = DM_TARGET_IMMUTABLE,
15555721d4e5SNathan Huckleberry .version = {1, 9, 0},
155603045cbaSSami Tolvanen .module = THIS_MODULE,
155703045cbaSSami Tolvanen .ctr = verity_ctr,
155803045cbaSSami Tolvanen .dtr = verity_dtr,
155903045cbaSSami Tolvanen .map = verity_map,
156003045cbaSSami Tolvanen .status = verity_status,
156103045cbaSSami Tolvanen .prepare_ioctl = verity_prepare_ioctl,
156203045cbaSSami Tolvanen .iterate_devices = verity_iterate_devices,
156303045cbaSSami Tolvanen .io_hints = verity_io_hints,
156403045cbaSSami Tolvanen };
15653664ff82SYangtao Li module_dm(verity);
156603045cbaSSami Tolvanen
1567dcb5620dSEric Biggers /*
1568dcb5620dSEric Biggers * Check whether a DM target is a verity target.
1569dcb5620dSEric Biggers */
dm_is_verity_target(struct dm_target * ti)1570dcb5620dSEric Biggers bool dm_is_verity_target(struct dm_target *ti)
1571dcb5620dSEric Biggers {
1572dcb5620dSEric Biggers return ti->type == &verity_target;
1573dcb5620dSEric Biggers }
1574dcb5620dSEric Biggers
157503045cbaSSami Tolvanen MODULE_AUTHOR("Mikulas Patocka <mpatocka@redhat.com>");
157603045cbaSSami Tolvanen MODULE_AUTHOR("Mandeep Baines <msb@chromium.org>");
157703045cbaSSami Tolvanen MODULE_AUTHOR("Will Drewry <wad@chromium.org>");
157803045cbaSSami Tolvanen MODULE_DESCRIPTION(DM_NAME " target for transparent disk integrity checking");
157903045cbaSSami Tolvanen MODULE_LICENSE("GPL");
1580