.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 Red Hat, Inc. |
---|
3 | 4 | * |
---|
4 | 5 | * Author: Mikulas Patocka <mpatocka@redhat.com> |
---|
5 | 6 | * |
---|
6 | 7 | * Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors |
---|
7 | | - * |
---|
8 | | - * This file is released under the GPLv2. |
---|
9 | 8 | * |
---|
10 | 9 | * In the file "/sys/module/dm_verity/parameters/prefetch_cluster" you can set |
---|
11 | 10 | * default prefetch value. Data are read in "prefetch_cluster" chunks from the |
---|
.. | .. |
---|
16 | 15 | |
---|
17 | 16 | #include "dm-verity.h" |
---|
18 | 17 | #include "dm-verity-fec.h" |
---|
19 | | - |
---|
| 18 | +#include "dm-verity-verify-sig.h" |
---|
20 | 19 | #include <linux/module.h> |
---|
21 | 20 | #include <linux/reboot.h> |
---|
22 | 21 | |
---|
.. | .. |
---|
31 | 30 | |
---|
32 | 31 | #define DM_VERITY_OPT_LOGGING "ignore_corruption" |
---|
33 | 32 | #define DM_VERITY_OPT_RESTART "restart_on_corruption" |
---|
| 33 | +#define DM_VERITY_OPT_PANIC "panic_on_corruption" |
---|
34 | 34 | #define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks" |
---|
35 | 35 | #define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once" |
---|
36 | 36 | |
---|
37 | | -#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC) |
---|
| 37 | +#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC + \ |
---|
| 38 | + DM_VERITY_ROOT_HASH_VERIFICATION_OPTS) |
---|
38 | 39 | |
---|
39 | 40 | static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; |
---|
40 | 41 | |
---|
.. | .. |
---|
251 | 252 | if (v->mode == DM_VERITY_MODE_LOGGING) |
---|
252 | 253 | return 0; |
---|
253 | 254 | |
---|
254 | | - if (v->mode == DM_VERITY_MODE_RESTART) { |
---|
255 | | -#ifdef CONFIG_DM_VERITY_AVB |
---|
256 | | - dm_verity_avb_error_handler(); |
---|
257 | | -#endif |
---|
| 255 | + if (v->mode == DM_VERITY_MODE_RESTART) |
---|
258 | 256 | kernel_restart("dm-verity device corrupted"); |
---|
259 | | - } |
---|
| 257 | + |
---|
| 258 | + if (v->mode == DM_VERITY_MODE_PANIC) |
---|
| 259 | + panic("dm-verity device corrupted"); |
---|
260 | 260 | |
---|
261 | 261 | return 1; |
---|
262 | 262 | } |
---|
.. | .. |
---|
482 | 482 | sector_t cur_block = io->block + b; |
---|
483 | 483 | struct ahash_request *req = verity_io_hash_req(v, io); |
---|
484 | 484 | |
---|
485 | | - if (v->validated_blocks && |
---|
| 485 | + if (v->validated_blocks && bio->bi_status == BLK_STS_OK && |
---|
486 | 486 | likely(test_bit(cur_block, v->validated_blocks))) { |
---|
487 | 487 | verity_bv_skip_block(v, io, &io->iter); |
---|
488 | 488 | continue; |
---|
.. | .. |
---|
538 | 538 | return -EIO; |
---|
539 | 539 | } |
---|
540 | 540 | if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA, |
---|
541 | | - cur_block)) |
---|
| 541 | + cur_block)) |
---|
542 | 542 | return -EIO; |
---|
543 | 543 | } |
---|
544 | 544 | } |
---|
.. | .. |
---|
634 | 634 | |
---|
635 | 635 | static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io) |
---|
636 | 636 | { |
---|
| 637 | + sector_t block = io->block; |
---|
| 638 | + unsigned int n_blocks = io->n_blocks; |
---|
637 | 639 | struct dm_verity_prefetch_work *pw; |
---|
| 640 | + |
---|
| 641 | + if (v->validated_blocks) { |
---|
| 642 | + while (n_blocks && test_bit(block, v->validated_blocks)) { |
---|
| 643 | + block++; |
---|
| 644 | + n_blocks--; |
---|
| 645 | + } |
---|
| 646 | + while (n_blocks && test_bit(block + n_blocks - 1, |
---|
| 647 | + v->validated_blocks)) |
---|
| 648 | + n_blocks--; |
---|
| 649 | + if (!n_blocks) |
---|
| 650 | + return; |
---|
| 651 | + } |
---|
638 | 652 | |
---|
639 | 653 | pw = kmalloc(sizeof(struct dm_verity_prefetch_work), |
---|
640 | 654 | GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); |
---|
.. | .. |
---|
644 | 658 | |
---|
645 | 659 | INIT_WORK(&pw->work, verity_prefetch_io); |
---|
646 | 660 | pw->v = v; |
---|
647 | | - pw->block = io->block; |
---|
648 | | - pw->n_blocks = io->n_blocks; |
---|
| 661 | + pw->block = block; |
---|
| 662 | + pw->n_blocks = n_blocks; |
---|
649 | 663 | queue_work(v->verify_wq, &pw->work); |
---|
650 | 664 | } |
---|
651 | 665 | |
---|
.. | .. |
---|
690 | 704 | |
---|
691 | 705 | verity_submit_prefetch(v, io); |
---|
692 | 706 | |
---|
693 | | - generic_make_request(bio); |
---|
| 707 | + submit_bio_noacct(bio); |
---|
694 | 708 | |
---|
695 | 709 | return DM_MAPIO_SUBMITTED; |
---|
696 | 710 | } |
---|
.. | .. |
---|
737 | 751 | args++; |
---|
738 | 752 | if (v->validated_blocks) |
---|
739 | 753 | args++; |
---|
| 754 | + if (v->signature_key_desc) |
---|
| 755 | + args += DM_VERITY_ROOT_HASH_VERIFICATION_OPTS; |
---|
740 | 756 | if (!args) |
---|
741 | 757 | return; |
---|
742 | 758 | DMEMIT(" %u", args); |
---|
.. | .. |
---|
749 | 765 | case DM_VERITY_MODE_RESTART: |
---|
750 | 766 | DMEMIT(DM_VERITY_OPT_RESTART); |
---|
751 | 767 | break; |
---|
| 768 | + case DM_VERITY_MODE_PANIC: |
---|
| 769 | + DMEMIT(DM_VERITY_OPT_PANIC); |
---|
| 770 | + break; |
---|
752 | 771 | default: |
---|
753 | 772 | BUG(); |
---|
754 | 773 | } |
---|
.. | .. |
---|
758 | 777 | if (v->validated_blocks) |
---|
759 | 778 | DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE); |
---|
760 | 779 | sz = verity_fec_status_table(v, sz, result, maxlen); |
---|
| 780 | + if (v->signature_key_desc) |
---|
| 781 | + DMEMIT(" " DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY |
---|
| 782 | + " %s", v->signature_key_desc); |
---|
761 | 783 | break; |
---|
762 | 784 | } |
---|
763 | 785 | } |
---|
.. | .. |
---|
823 | 845 | |
---|
824 | 846 | verity_fec_dtr(v); |
---|
825 | 847 | |
---|
| 848 | + kfree(v->signature_key_desc); |
---|
| 849 | + |
---|
826 | 850 | kfree(v); |
---|
827 | 851 | } |
---|
828 | 852 | |
---|
.. | .. |
---|
878 | 902 | return r; |
---|
879 | 903 | } |
---|
880 | 904 | |
---|
881 | | -static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v) |
---|
| 905 | +static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, |
---|
| 906 | + struct dm_verity_sig_opts *verify_args) |
---|
882 | 907 | { |
---|
883 | 908 | int r; |
---|
884 | 909 | unsigned argc; |
---|
.. | .. |
---|
908 | 933 | v->mode = DM_VERITY_MODE_RESTART; |
---|
909 | 934 | continue; |
---|
910 | 935 | |
---|
| 936 | + } else if (!strcasecmp(arg_name, DM_VERITY_OPT_PANIC)) { |
---|
| 937 | + v->mode = DM_VERITY_MODE_PANIC; |
---|
| 938 | + continue; |
---|
| 939 | + |
---|
911 | 940 | } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) { |
---|
912 | 941 | r = verity_alloc_zero_digest(v); |
---|
913 | 942 | if (r) { |
---|
.. | .. |
---|
927 | 956 | if (r) |
---|
928 | 957 | return r; |
---|
929 | 958 | continue; |
---|
| 959 | + } else if (verity_verify_is_sig_opt_arg(arg_name)) { |
---|
| 960 | + r = verity_verify_sig_parse_opt_args(as, v, |
---|
| 961 | + verify_args, |
---|
| 962 | + &argc, arg_name); |
---|
| 963 | + if (r) |
---|
| 964 | + return r; |
---|
| 965 | + continue; |
---|
| 966 | + |
---|
930 | 967 | } |
---|
931 | 968 | |
---|
932 | 969 | ti->error = "Unrecognized verity feature request"; |
---|
.. | .. |
---|
953 | 990 | static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) |
---|
954 | 991 | { |
---|
955 | 992 | struct dm_verity *v; |
---|
| 993 | + struct dm_verity_sig_opts verify_args = {0}; |
---|
956 | 994 | struct dm_arg_set as; |
---|
957 | 995 | unsigned int num; |
---|
958 | 996 | unsigned long long num_ll; |
---|
.. | .. |
---|
960 | 998 | int i; |
---|
961 | 999 | sector_t hash_position; |
---|
962 | 1000 | char dummy; |
---|
| 1001 | + char *root_hash_digest_to_validate; |
---|
963 | 1002 | |
---|
964 | 1003 | v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL); |
---|
965 | 1004 | if (!v) { |
---|
.. | .. |
---|
1093 | 1132 | r = -EINVAL; |
---|
1094 | 1133 | goto bad; |
---|
1095 | 1134 | } |
---|
| 1135 | + root_hash_digest_to_validate = argv[8]; |
---|
1096 | 1136 | |
---|
1097 | 1137 | if (strcmp(argv[9], "-")) { |
---|
1098 | 1138 | v->salt_size = strlen(argv[9]) / 2; |
---|
.. | .. |
---|
1118 | 1158 | as.argc = argc; |
---|
1119 | 1159 | as.argv = argv; |
---|
1120 | 1160 | |
---|
1121 | | - r = verity_parse_opt_args(&as, v); |
---|
| 1161 | + r = verity_parse_opt_args(&as, v, &verify_args); |
---|
1122 | 1162 | if (r < 0) |
---|
1123 | 1163 | goto bad; |
---|
1124 | 1164 | } |
---|
1125 | 1165 | |
---|
1126 | | -#ifdef CONFIG_DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED |
---|
1127 | | - if (!v->validated_blocks) { |
---|
1128 | | - r = verity_alloc_most_once(v); |
---|
1129 | | - if (r) |
---|
1130 | | - goto bad; |
---|
| 1166 | + /* Root hash signature is a optional parameter*/ |
---|
| 1167 | + r = verity_verify_root_hash(root_hash_digest_to_validate, |
---|
| 1168 | + strlen(root_hash_digest_to_validate), |
---|
| 1169 | + verify_args.sig, |
---|
| 1170 | + verify_args.sig_size); |
---|
| 1171 | + if (r < 0) { |
---|
| 1172 | + ti->error = "Root hash verification failed"; |
---|
| 1173 | + goto bad; |
---|
1131 | 1174 | } |
---|
1132 | | -#endif |
---|
1133 | | - |
---|
1134 | 1175 | v->hash_per_block_bits = |
---|
1135 | 1176 | __fls((1 << v->hash_dev_block_bits) / v->digest_size); |
---|
1136 | 1177 | |
---|
.. | .. |
---|
1178 | 1219 | goto bad; |
---|
1179 | 1220 | } |
---|
1180 | 1221 | |
---|
1181 | | - /* WQ_UNBOUND greatly improves performance when running on ramdisk */ |
---|
1182 | | - v->verify_wq = alloc_workqueue("kverityd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, num_online_cpus()); |
---|
| 1222 | + /* |
---|
| 1223 | + * Using WQ_HIGHPRI improves throughput and completion latency by |
---|
| 1224 | + * reducing wait times when reading from a dm-verity device. |
---|
| 1225 | + * |
---|
| 1226 | + * Also as required for the "try_verify_in_tasklet" feature: WQ_HIGHPRI |
---|
| 1227 | + * allows verify_wq to preempt softirq since verification in tasklet |
---|
| 1228 | + * will fall-back to using it for error handling (or if the bufio cache |
---|
| 1229 | + * doesn't have required hashes). |
---|
| 1230 | + */ |
---|
| 1231 | + v->verify_wq = alloc_workqueue("kverityd", WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); |
---|
1183 | 1232 | if (!v->verify_wq) { |
---|
1184 | 1233 | ti->error = "Cannot allocate workqueue"; |
---|
1185 | 1234 | r = -ENOMEM; |
---|
.. | .. |
---|
1196 | 1245 | ti->per_io_data_size = roundup(ti->per_io_data_size, |
---|
1197 | 1246 | __alignof__(struct dm_verity_io)); |
---|
1198 | 1247 | |
---|
| 1248 | + verity_verify_sig_opts_cleanup(&verify_args); |
---|
| 1249 | + |
---|
1199 | 1250 | return 0; |
---|
1200 | 1251 | |
---|
1201 | 1252 | bad: |
---|
| 1253 | + |
---|
| 1254 | + verity_verify_sig_opts_cleanup(&verify_args); |
---|
1202 | 1255 | verity_dtr(ti); |
---|
1203 | 1256 | |
---|
1204 | 1257 | return r; |
---|
.. | .. |
---|
1206 | 1259 | |
---|
1207 | 1260 | static struct target_type verity_target = { |
---|
1208 | 1261 | .name = "verity", |
---|
1209 | | - .version = {1, 4, 0}, |
---|
| 1262 | + .features = DM_TARGET_IMMUTABLE, |
---|
| 1263 | + .version = {1, 7, 0}, |
---|
| 1264 | + .features = DM_TARGET_IMMUTABLE, |
---|
1210 | 1265 | .module = THIS_MODULE, |
---|
1211 | 1266 | .ctr = verity_ctr, |
---|
1212 | 1267 | .dtr = verity_dtr, |
---|