2019-06-01 11:08:42 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-03-28 21:43:38 +04:00
/*
* Copyright ( C ) 2012 Red Hat , Inc .
*
* Author : Mikulas Patocka < mpatocka @ redhat . com >
*
* Based on Chromium dm - verity driver ( C ) 2011 The Chromium OS Authors
*
* In the file " /sys/module/dm_verity/parameters/prefetch_cluster " you can set
* default prefetch value . Data are read in " prefetch_cluster " chunks from the
* hash device . Setting this greatly improves performance when data and hash
* are on the same disk on different partitions on devices with poor random
* access behavior .
*/
2015-12-04 00:01:51 +03:00
# include "dm-verity.h"
2015-12-03 17:26:30 +03:00
# include "dm-verity-fec.h"
2019-07-18 03:46:15 +03:00
# include "dm-verity-verify-sig.h"
2012-03-28 21:43:38 +04:00
# include <linux/module.h>
2015-03-18 18:52:14 +03:00
# include <linux/reboot.h>
2021-09-20 15:33:28 +03:00
# include <linux/scatterlist.h>
2022-06-27 18:35:24 +03:00
# include <linux/string.h>
2022-07-25 23:52:17 +03:00
# include <linux/jump_label.h>
2012-03-28 21:43:38 +04:00
# define DM_MSG_PREFIX "verity"
2015-03-18 18:52:14 +03:00
# define DM_VERITY_ENV_LENGTH 42
# define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR"
2012-03-28 21:43:38 +04:00
# define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
2015-03-18 18:52:14 +03:00
# define DM_VERITY_MAX_CORRUPTED_ERRS 100
# define DM_VERITY_OPT_LOGGING "ignore_corruption"
# define DM_VERITY_OPT_RESTART "restart_on_corruption"
2020-06-18 09:56:50 +03:00
# define DM_VERITY_OPT_PANIC "panic_on_corruption"
2015-12-03 17:26:31 +03:00
# define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
2018-03-23 04:18:04 +03:00
# define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
2022-07-22 12:38:23 +03:00
# define DM_VERITY_OPT_TASKLET_VERIFY "try_verify_in_tasklet"
2012-03-28 21:43:38 +04:00
2022-08-10 00:33:12 +03:00
# define DM_VERITY_OPTS_MAX (4 + DM_VERITY_OPTS_FEC + \
2019-07-18 03:46:15 +03:00
DM_VERITY_ROOT_HASH_VERIFICATION_OPTS )
2015-11-05 05:02:32 +03:00
2023-01-25 23:14:58 +03:00
static unsigned int dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE ;
2012-03-28 21:43:38 +04:00
2023-02-07 01:58:05 +03:00
module_param_named ( prefetch_cluster , dm_verity_prefetch_cluster , uint , 0644 ) ;
2012-03-28 21:43:38 +04:00
2022-07-25 23:52:17 +03:00
static DEFINE_STATIC_KEY_FALSE ( use_tasklet_enabled ) ;
2013-03-20 21:21:25 +04:00
struct dm_verity_prefetch_work {
struct work_struct work ;
struct dm_verity * v ;
sector_t block ;
2023-01-25 23:14:58 +03:00
unsigned int n_blocks ;
2013-03-20 21:21:25 +04:00
} ;
2012-03-28 21:43:38 +04:00
/*
* Auxiliary structure appended to each dm - bufio buffer . If the value
* hash_verified is nonzero , hash of the block has been verified .
*
* The variable hash_verified is set to 0 when allocating the buffer , then
* it can be changed to 1 and it is never reset to 0 again .
*
* There is no lock around this value , a race condition can at worst cause
* that multiple processes verify the hash of the same buffer simultaneously
* and write 1 to hash_verified simultaneously .
* This condition is harmless , so we don ' t need locking .
*/
struct buffer_aux {
int hash_verified ;
} ;
/*
* Initialize struct buffer_aux for a freshly created buffer .
*/
static void dm_bufio_alloc_callback ( struct dm_buffer * buf )
{
struct buffer_aux * aux = dm_bufio_get_aux_data ( buf ) ;
aux - > hash_verified = 0 ;
}
/*
* Translate input sector number to the sector number on the target device .
*/
static sector_t verity_map_sector ( struct dm_verity * v , sector_t bi_sector )
{
return v - > data_start + dm_target_offset ( v - > ti , bi_sector ) ;
}
/*
* Return hash position of a specified block at a specified tree level
* ( 0 is the lowest level ) .
* The lowest " hash_per_block_bits " - bits of the result denote hash position
* inside a hash block . The remaining bits denote location of the hash block .
*/
static sector_t verity_position_at_level ( struct dm_verity * v , sector_t block ,
int level )
{
return block > > ( level * v - > hash_per_block_bits ) ;
}
2017-02-19 15:46:07 +03:00
static int verity_hash_update ( struct dm_verity * v , struct ahash_request * req ,
const u8 * data , size_t len ,
2017-10-18 10:00:45 +03:00
struct crypto_wait * wait )
2015-11-05 05:02:31 +03:00
{
2017-02-19 15:46:07 +03:00
struct scatterlist sg ;
2015-11-05 05:02:31 +03:00
2018-08-22 19:45:51 +03:00
if ( likely ( ! is_vmalloc_addr ( data ) ) ) {
sg_init_one ( & sg , data , len ) ;
ahash_request_set_crypt ( req , & sg , NULL , len ) ;
return crypto_wait_req ( crypto_ahash_update ( req ) , wait ) ;
}
2023-02-07 22:35:25 +03:00
do {
int r ;
size_t this_step = min_t ( size_t , len , PAGE_SIZE - offset_in_page ( data ) ) ;
flush_kernel_vmap_range ( ( void * ) data , this_step ) ;
sg_init_table ( & sg , 1 ) ;
sg_set_page ( & sg , vmalloc_to_page ( data ) , this_step , offset_in_page ( data ) ) ;
ahash_request_set_crypt ( req , & sg , NULL , this_step ) ;
r = crypto_wait_req ( crypto_ahash_update ( req ) , wait ) ;
if ( unlikely ( r ) )
return r ;
data + = this_step ;
len - = this_step ;
} while ( len ) ;
return 0 ;
2017-02-19 15:46:07 +03:00
}
/*
* Wrapper for crypto_ahash_init , which handles verity salting .
*/
static int verity_hash_init ( struct dm_verity * v , struct ahash_request * req ,
2017-10-18 10:00:45 +03:00
struct crypto_wait * wait )
2017-02-19 15:46:07 +03:00
{
int r ;
ahash_request_set_tfm ( req , v - > tfm ) ;
ahash_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG ,
2017-10-18 10:00:45 +03:00
crypto_req_done , ( void * ) wait ) ;
crypto_init_wait ( wait ) ;
2017-02-19 15:46:07 +03:00
2017-10-18 10:00:45 +03:00
r = crypto_wait_req ( crypto_ahash_init ( req ) , wait ) ;
2017-02-19 15:46:07 +03:00
if ( unlikely ( r < 0 ) ) {
DMERR ( " crypto_ahash_init failed: %d " , r ) ;
return r ;
}
2017-05-18 13:47:25 +03:00
if ( likely ( v - > salt_size & & ( v - > version > = 1 ) ) )
2017-10-18 10:00:45 +03:00
r = verity_hash_update ( v , req , v - > salt , v - > salt_size , wait ) ;
2015-11-05 05:02:31 +03:00
return r ;
}
2017-02-19 15:46:07 +03:00
static int verity_hash_final ( struct dm_verity * v , struct ahash_request * req ,
2017-10-18 10:00:45 +03:00
u8 * digest , struct crypto_wait * wait )
2015-11-05 05:02:31 +03:00
{
int r ;
2017-05-18 13:47:25 +03:00
if ( unlikely ( v - > salt_size & & ( ! v - > version ) ) ) {
2017-10-18 10:00:45 +03:00
r = verity_hash_update ( v , req , v - > salt , v - > salt_size , wait ) ;
2015-11-05 05:02:31 +03:00
if ( r < 0 ) {
2023-02-07 01:42:32 +03:00
DMERR ( " %s failed updating salt: %d " , __func__ , r ) ;
2017-02-19 15:46:07 +03:00
goto out ;
2015-11-05 05:02:31 +03:00
}
}
2017-02-19 15:46:07 +03:00
ahash_request_set_crypt ( req , NULL , digest , 0 ) ;
2017-10-18 10:00:45 +03:00
r = crypto_wait_req ( crypto_ahash_final ( req ) , wait ) ;
2017-02-19 15:46:07 +03:00
out :
2015-11-05 05:02:31 +03:00
return r ;
}
2017-02-19 15:46:07 +03:00
int verity_hash ( struct dm_verity * v , struct ahash_request * req ,
2015-12-04 00:01:51 +03:00
const u8 * data , size_t len , u8 * digest )
2015-11-05 05:02:31 +03:00
{
int r ;
2017-10-18 10:00:45 +03:00
struct crypto_wait wait ;
2015-11-05 05:02:31 +03:00
2017-10-18 10:00:45 +03:00
r = verity_hash_init ( v , req , & wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2017-02-19 15:46:07 +03:00
goto out ;
2015-11-05 05:02:31 +03:00
2017-10-18 10:00:45 +03:00
r = verity_hash_update ( v , req , data , len , & wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2017-02-19 15:46:07 +03:00
goto out ;
2017-10-18 10:00:45 +03:00
r = verity_hash_final ( v , req , digest , & wait ) ;
2015-11-05 05:02:31 +03:00
2017-02-19 15:46:07 +03:00
out :
return r ;
2015-11-05 05:02:31 +03:00
}
2012-03-28 21:43:38 +04:00
static void verity_hash_at_level ( struct dm_verity * v , sector_t block , int level ,
2023-01-25 23:14:58 +03:00
sector_t * hash_block , unsigned int * offset )
2012-03-28 21:43:38 +04:00
{
sector_t position = verity_position_at_level ( v , block , level ) ;
2023-01-25 23:14:58 +03:00
unsigned int idx ;
2012-03-28 21:43:38 +04:00
* hash_block = v - > hash_level_block [ level ] + ( position > > v - > hash_per_block_bits ) ;
if ( ! offset )
return ;
idx = position & ( ( 1 < < v - > hash_per_block_bits ) - 1 ) ;
if ( ! v - > version )
* offset = idx * v - > digest_size ;
else
* offset = idx < < ( v - > hash_dev_block_bits - v - > hash_per_block_bits ) ;
}
2015-03-18 18:52:14 +03:00
/*
* Handle verification errors .
*/
static int verity_handle_err ( struct dm_verity * v , enum verity_block_type type ,
unsigned long long block )
{
char verity_env [ DM_VERITY_ENV_LENGTH ] ;
char * envp [ ] = { verity_env , NULL } ;
const char * type_str = " " ;
struct mapped_device * md = dm_table_get_md ( v - > ti - > table ) ;
/* Corruption should be visible in device status in all modes */
2022-07-22 12:38:23 +03:00
v - > hash_failed = true ;
2015-03-18 18:52:14 +03:00
if ( v - > corrupted_errs > = DM_VERITY_MAX_CORRUPTED_ERRS )
goto out ;
v - > corrupted_errs + + ;
switch ( type ) {
case DM_VERITY_BLOCK_TYPE_DATA :
type_str = " data " ;
break ;
case DM_VERITY_BLOCK_TYPE_METADATA :
type_str = " metadata " ;
break ;
default :
BUG ( ) ;
}
2019-06-20 14:00:19 +03:00
DMERR_LIMIT ( " %s: %s block %llu is corrupted " , v - > data_dev - > name ,
type_str , block ) ;
2015-03-18 18:52:14 +03:00
if ( v - > corrupted_errs = = DM_VERITY_MAX_CORRUPTED_ERRS )
DMERR ( " %s: reached maximum errors " , v - > data_dev - > name ) ;
snprintf ( verity_env , DM_VERITY_ENV_LENGTH , " %s=%d,%llu " ,
DM_VERITY_ENV_VAR_NAME , type , block ) ;
kobject_uevent_env ( & disk_to_dev ( dm_disk ( md ) ) - > kobj , KOBJ_CHANGE , envp ) ;
out :
if ( v - > mode = = DM_VERITY_MODE_LOGGING )
return 0 ;
if ( v - > mode = = DM_VERITY_MODE_RESTART )
kernel_restart ( " dm-verity device corrupted " ) ;
2020-06-18 09:56:50 +03:00
if ( v - > mode = = DM_VERITY_MODE_PANIC )
panic ( " dm-verity device corrupted " ) ;
2015-03-18 18:52:14 +03:00
return 1 ;
}
2012-03-28 21:43:38 +04:00
/*
* Verify hash of a metadata block pertaining to the specified data block
* ( " block " argument ) at a specified level ( " level " argument ) .
*
2015-12-04 00:01:51 +03:00
* On successful return , verity_io_want_digest ( v , io ) contains the hash value
* for a lower tree level or for the data block ( if we ' re at the lowest level ) .
2012-03-28 21:43:38 +04:00
*
* If " skip_unverified " is true , unverified buffer is skipped and 1 is returned .
* If " skip_unverified " is false , unverified buffer is hashed and verified
2015-12-04 00:01:51 +03:00
* against current value of verity_io_want_digest ( v , io ) .
2012-03-28 21:43:38 +04:00
*/
2015-11-05 05:02:31 +03:00
static int verity_verify_level ( struct dm_verity * v , struct dm_verity_io * io ,
sector_t block , int level , bool skip_unverified ,
u8 * want_digest )
2012-03-28 21:43:38 +04:00
{
struct dm_buffer * buf ;
struct buffer_aux * aux ;
u8 * data ;
int r ;
sector_t hash_block ;
2023-01-25 23:14:58 +03:00
unsigned int offset ;
2012-03-28 21:43:38 +04:00
verity_hash_at_level ( v , block , level , & hash_block , & offset ) ;
2022-07-25 23:52:17 +03:00
if ( static_branch_unlikely ( & use_tasklet_enabled ) & & io - > in_tasklet ) {
2022-07-22 12:38:23 +03:00
data = dm_bufio_get ( v - > bufio , hash_block , & buf ) ;
if ( data = = NULL ) {
/*
* In tasklet and the hash was not in the bufio cache .
* Return early and resume execution from a work - queue
* to read the hash from disk .
*/
return - EAGAIN ;
}
} else
data = dm_bufio_read ( v - > bufio , hash_block , & buf ) ;
2015-08-10 09:12:26 +03:00
if ( IS_ERR ( data ) )
2012-03-28 21:43:38 +04:00
return PTR_ERR ( data ) ;
aux = dm_bufio_get_aux_data ( buf ) ;
if ( ! aux - > hash_verified ) {
if ( skip_unverified ) {
r = 1 ;
goto release_ret_r ;
}
2017-02-19 15:46:07 +03:00
r = verity_hash ( v , verity_io_hash_req ( v , io ) ,
2015-11-05 05:02:31 +03:00
data , 1 < < v - > hash_dev_block_bits ,
2015-12-04 00:01:51 +03:00
verity_io_real_digest ( v , io ) ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2012-03-28 21:43:38 +04:00
goto release_ret_r ;
2015-12-04 00:01:51 +03:00
if ( likely ( memcmp ( verity_io_real_digest ( v , io ) , want_digest ,
2015-11-05 05:02:31 +03:00
v - > digest_size ) = = 0 ) )
aux - > hash_verified = 1 ;
2022-07-25 23:52:17 +03:00
else if ( static_branch_unlikely ( & use_tasklet_enabled ) & &
io - > in_tasklet ) {
2022-07-22 12:38:23 +03:00
/*
* Error handling code ( FEC included ) cannot be run in a
* tasklet since it may sleep , so fallback to work - queue .
*/
r = - EAGAIN ;
goto release_ret_r ;
2023-01-31 00:13:54 +03:00
} else if ( verity_fec_decode ( v , io , DM_VERITY_BLOCK_TYPE_METADATA ,
hash_block , data , NULL ) = = 0 )
2015-12-03 17:26:30 +03:00
aux - > hash_verified = 1 ;
2015-11-05 05:02:31 +03:00
else if ( verity_handle_err ( v ,
DM_VERITY_BLOCK_TYPE_METADATA ,
hash_block ) ) {
r = - EIO ;
2012-03-28 21:43:38 +04:00
goto release_ret_r ;
}
}
data + = offset ;
2015-11-05 05:02:31 +03:00
memcpy ( want_digest , data , v - > digest_size ) ;
r = 0 ;
2012-03-28 21:43:38 +04:00
release_ret_r :
dm_bufio_release ( buf ) ;
return r ;
}
2015-11-05 05:02:31 +03:00
/*
* Find a hash for a given block , write it to digest and verify the integrity
* of the hash tree if necessary .
*/
2015-12-04 00:01:51 +03:00
int verity_hash_for_block ( struct dm_verity * v , struct dm_verity_io * io ,
2015-12-03 17:26:31 +03:00
sector_t block , u8 * digest , bool * is_zero )
2015-11-05 05:02:31 +03:00
{
2015-12-03 17:26:31 +03:00
int r = 0 , i ;
2015-11-05 05:02:31 +03:00
if ( likely ( v - > levels ) ) {
/*
* First , we try to get the requested hash for
* the current block . If the hash block itself is
* verified , zero is returned . If it isn ' t , this
* function returns 1 and we fall back to whole
* chain verification .
*/
r = verity_verify_level ( v , io , block , 0 , true , digest ) ;
if ( likely ( r < = 0 ) )
2015-12-03 17:26:31 +03:00
goto out ;
2015-11-05 05:02:31 +03:00
}
memcpy ( digest , v - > root_digest , v - > digest_size ) ;
for ( i = v - > levels - 1 ; i > = 0 ; i - - ) {
r = verity_verify_level ( v , io , block , i , false , digest ) ;
if ( unlikely ( r ) )
2015-12-03 17:26:31 +03:00
goto out ;
2015-11-05 05:02:31 +03:00
}
2015-12-03 17:26:31 +03:00
out :
if ( ! r & & v - > zero_digest )
* is_zero = ! memcmp ( v - > zero_digest , digest , v - > digest_size ) ;
else
* is_zero = false ;
2015-11-05 05:02:31 +03:00
2015-12-03 17:26:31 +03:00
return r ;
2015-11-05 05:02:31 +03:00
}
2017-02-19 15:46:07 +03:00
/*
* Calculates the digest for the given bio
*/
2018-03-28 14:11:58 +03:00
static int verity_for_io_block ( struct dm_verity * v , struct dm_verity_io * io ,
struct bvec_iter * iter , struct crypto_wait * wait )
2017-02-19 15:46:07 +03:00
{
unsigned int todo = 1 < < v - > data_dev_block_bits ;
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
struct scatterlist sg ;
struct ahash_request * req = verity_io_hash_req ( v , io ) ;
do {
int r ;
unsigned int len ;
struct bio_vec bv = bio_iter_iovec ( bio , * iter ) ;
sg_init_table ( & sg , 1 ) ;
len = bv . bv_len ;
if ( likely ( len > = todo ) )
len = todo ;
/*
* Operating on a single page at a time looks suboptimal
* until you consider the typical block size is 4 , 096 B .
* Going through this loops twice should be very rare .
*/
sg_set_page ( & sg , bv . bv_page , len , bv . bv_offset ) ;
ahash_request_set_crypt ( req , & sg , NULL , len ) ;
2017-10-18 10:00:45 +03:00
r = crypto_wait_req ( crypto_ahash_update ( req ) , wait ) ;
2017-02-19 15:46:07 +03:00
if ( unlikely ( r < 0 ) ) {
2023-02-07 01:42:32 +03:00
DMERR ( " %s crypto op failed: %d " , __func__ , r ) ;
2017-02-19 15:46:07 +03:00
return r ;
}
bio_advance_iter ( bio , iter , len ) ;
todo - = len ;
} while ( todo ) ;
return 0 ;
}
2015-12-04 00:30:36 +03:00
/*
* Calls function process for 1 < < v - > data_dev_block_bits bytes in the bio_vec
* starting from iter .
*/
int 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 ) )
{
2023-01-25 23:14:58 +03:00
unsigned int todo = 1 < < v - > data_dev_block_bits ;
2016-01-31 21:28:26 +03:00
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
2015-12-04 00:30:36 +03:00
do {
int r ;
u8 * page ;
2023-01-25 23:14:58 +03:00
unsigned int len ;
2015-12-04 00:30:36 +03:00
struct bio_vec bv = bio_iter_iovec ( bio , * iter ) ;
2021-10-19 16:44:06 +03:00
page = bvec_kmap_local ( & bv ) ;
2015-12-04 00:30:36 +03:00
len = bv . bv_len ;
if ( likely ( len > = todo ) )
len = todo ;
2021-10-19 16:44:06 +03:00
r = process ( v , io , page , len ) ;
kunmap_local ( page ) ;
2015-12-04 00:30:36 +03:00
if ( r < 0 )
return r ;
bio_advance_iter ( bio , iter , len ) ;
todo - = len ;
} while ( todo ) ;
return 0 ;
}
2015-12-03 17:26:31 +03:00
static int verity_bv_zero ( struct dm_verity * v , struct dm_verity_io * io ,
u8 * data , size_t len )
{
memset ( data , 0 , len ) ;
return 0 ;
}
2018-03-23 04:18:04 +03:00
/*
* Moves the bio iter one data block forward .
*/
static inline void verity_bv_skip_block ( struct dm_verity * v ,
struct dm_verity_io * io ,
struct bvec_iter * iter )
{
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
bio_advance_iter ( bio , iter , 1 < < v - > data_dev_block_bits ) ;
}
2012-03-28 21:43:38 +04:00
/*
* Verify one " dm_verity_io " structure .
*/
static int verity_verify_io ( struct dm_verity_io * io )
{
2015-12-03 17:26:31 +03:00
bool is_zero ;
2012-03-28 21:43:38 +04:00
struct dm_verity * v = io - > v ;
2022-08-04 05:43:22 +03:00
# if defined(CONFIG_DM_VERITY_FEC)
2015-12-04 00:30:36 +03:00
struct bvec_iter start ;
2022-08-04 05:43:22 +03:00
# endif
2022-08-04 20:37:53 +03:00
struct bvec_iter iter_copy ;
struct bvec_iter * iter ;
2017-10-18 10:00:45 +03:00
struct crypto_wait wait ;
2021-09-13 12:26:42 +03:00
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
2022-07-22 12:38:23 +03:00
unsigned int b ;
2012-03-28 21:43:38 +04:00
2022-08-04 20:37:53 +03:00
if ( static_branch_unlikely ( & use_tasklet_enabled ) & & io - > in_tasklet ) {
/*
* Copy the iterator in case we need to restart
* verification in a work - queue .
*/
iter_copy = io - > iter ;
iter = & iter_copy ;
} else
iter = & io - > iter ;
2012-03-28 21:43:38 +04:00
for ( b = 0 ; b < io - > n_blocks ; b + + ) {
int r ;
2018-03-23 04:18:04 +03:00
sector_t cur_block = io - > block + b ;
2017-02-19 15:46:07 +03:00
struct ahash_request * req = verity_io_hash_req ( v , io ) ;
2012-03-28 21:43:38 +04:00
2018-03-23 04:18:04 +03:00
if ( v - > validated_blocks & &
likely ( test_bit ( cur_block , v - > validated_blocks ) ) ) {
2022-08-04 20:37:53 +03:00
verity_bv_skip_block ( v , io , iter ) ;
2018-03-23 04:18:04 +03:00
continue ;
}
r = verity_hash_for_block ( v , io , cur_block ,
2015-12-03 17:26:31 +03:00
verity_io_want_digest ( v , io ) ,
& is_zero ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
return r ;
2012-03-28 21:43:38 +04:00
2015-12-03 17:26:31 +03:00
if ( is_zero ) {
/*
* If we expect a zero block , don ' t validate , just
* return zeros .
*/
2022-08-04 20:37:53 +03:00
r = verity_for_bv_block ( v , io , iter ,
2015-12-03 17:26:31 +03:00
verity_bv_zero ) ;
if ( unlikely ( r < 0 ) )
return r ;
continue ;
}
2017-10-18 10:00:45 +03:00
r = verity_hash_init ( v , req , & wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2012-03-28 21:43:38 +04:00
return r ;
2022-08-04 05:43:22 +03:00
# if defined(CONFIG_DM_VERITY_FEC)
if ( verity_fec_is_enabled ( v ) )
2022-08-04 20:37:53 +03:00
start = * iter ;
2022-08-04 05:43:22 +03:00
# endif
2022-08-04 20:37:53 +03:00
r = verity_for_io_block ( v , io , iter , & wait ) ;
2015-12-04 00:30:36 +03:00
if ( unlikely ( r < 0 ) )
return r ;
2012-03-28 21:43:38 +04:00
2017-02-19 15:46:07 +03:00
r = verity_hash_final ( v , req , verity_io_real_digest ( v , io ) ,
2017-10-18 10:00:45 +03:00
& wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2012-03-28 21:43:38 +04:00
return r ;
2015-11-05 05:02:31 +03:00
2015-12-04 00:01:51 +03:00
if ( likely ( memcmp ( verity_io_real_digest ( v , io ) ,
2018-03-23 04:18:04 +03:00
verity_io_want_digest ( v , io ) , v - > digest_size ) = = 0 ) ) {
if ( v - > validated_blocks )
set_bit ( cur_block , v - > validated_blocks ) ;
2015-11-05 05:02:31 +03:00
continue ;
2022-07-25 23:52:17 +03:00
} else if ( static_branch_unlikely ( & use_tasklet_enabled ) & &
io - > in_tasklet ) {
2022-07-22 12:38:23 +03:00
/*
* Error handling code ( FEC included ) cannot be run in a
* tasklet since it may sleep , so fallback to work - queue .
*/
return - EAGAIN ;
2022-08-04 05:43:22 +03:00
# if defined(CONFIG_DM_VERITY_FEC)
2022-06-15 03:51:51 +03:00
} else if ( verity_fec_decode ( v , io , DM_VERITY_BLOCK_TYPE_DATA ,
2022-07-22 12:38:23 +03:00
cur_block , NULL , & start ) = = 0 ) {
2015-12-03 17:26:30 +03:00
continue ;
2022-08-04 05:43:22 +03:00
# endif
2022-06-15 03:51:51 +03:00
} else {
2021-09-13 12:26:42 +03:00
if ( bio - > bi_status ) {
/*
* Error correction failed ; Just return error
*/
return - EIO ;
}
if ( verity_handle_err ( v , DM_VERITY_BLOCK_TYPE_DATA ,
cur_block ) )
return - EIO ;
}
2012-03-28 21:43:38 +04:00
}
return 0 ;
}
2020-12-03 03:46:59 +03:00
/*
* Skip verity work in response to I / O error when system is shutting down .
*/
static inline bool verity_is_system_shutting_down ( void )
{
return system_state = = SYSTEM_HALT | | system_state = = SYSTEM_POWER_OFF
| | system_state = = SYSTEM_RESTART ;
}
2012-03-28 21:43:38 +04:00
/*
* End one " io " structure with a given error .
*/
2017-06-03 10:38:06 +03:00
static void verity_finish_io ( struct dm_verity_io * io , blk_status_t status )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = io - > v ;
2016-01-31 21:28:26 +03:00
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
2012-03-28 21:43:38 +04:00
bio - > bi_end_io = io - > orig_bi_end_io ;
2017-06-03 10:38:06 +03:00
bio - > bi_status = status ;
2012-03-28 21:43:38 +04:00
2022-07-25 23:52:17 +03:00
if ( ! static_branch_unlikely ( & use_tasklet_enabled ) | | ! io - > in_tasklet )
2022-07-22 12:38:23 +03:00
verity_fec_finish_io ( io ) ;
2015-12-03 17:26:30 +03:00
2015-07-20 16:29:37 +03:00
bio_endio ( bio ) ;
2012-03-28 21:43:38 +04:00
}
static void verity_work ( struct work_struct * w )
{
struct dm_verity_io * io = container_of ( w , struct dm_verity_io , work ) ;
2022-07-22 12:38:23 +03:00
io - > in_tasklet = false ;
verity_fec_init_io ( io ) ;
2017-06-03 10:38:06 +03:00
verity_finish_io ( io , errno_to_blk_status ( verity_verify_io ( io ) ) ) ;
2012-03-28 21:43:38 +04:00
}
2022-07-22 12:38:23 +03:00
static void verity_tasklet ( unsigned long data )
{
struct dm_verity_io * io = ( struct dm_verity_io * ) data ;
int err ;
io - > in_tasklet = true ;
err = verity_verify_io ( io ) ;
if ( err = = - EAGAIN ) {
/* fallback to retrying with work-queue */
INIT_WORK ( & io - > work , verity_work ) ;
queue_work ( io - > v - > verify_wq , & io - > work ) ;
return ;
}
verity_finish_io ( io , errno_to_blk_status ( err ) ) ;
}
2015-07-20 16:29:37 +03:00
static void verity_end_io ( struct bio * bio )
2012-03-28 21:43:38 +04:00
{
struct dm_verity_io * io = bio - > bi_private ;
2020-12-03 03:46:59 +03:00
if ( bio - > bi_status & &
( ! verity_fec_is_enabled ( io - > v ) | | verity_is_system_shutting_down ( ) ) ) {
2017-06-03 10:38:06 +03:00
verity_finish_io ( io , bio - > bi_status ) ;
2012-03-28 21:43:38 +04:00
return ;
}
2022-07-25 23:52:17 +03:00
if ( static_branch_unlikely ( & use_tasklet_enabled ) & & io - > v - > use_tasklet ) {
2022-07-22 12:38:23 +03:00
tasklet_init ( & io - > tasklet , verity_tasklet , ( unsigned long ) io ) ;
tasklet_schedule ( & io - > tasklet ) ;
} else {
INIT_WORK ( & io - > work , verity_work ) ;
queue_work ( io - > v - > verify_wq , & io - > work ) ;
}
2012-03-28 21:43:38 +04:00
}
/*
* Prefetch buffers for the specified io .
* The root buffer is not prefetched , it is assumed that it will be cached
* all the time .
*/
2013-03-20 21:21:25 +04:00
static void verity_prefetch_io ( struct work_struct * work )
2012-03-28 21:43:38 +04:00
{
2013-03-20 21:21:25 +04:00
struct dm_verity_prefetch_work * pw =
container_of ( work , struct dm_verity_prefetch_work , work ) ;
struct dm_verity * v = pw - > v ;
2012-03-28 21:43:38 +04:00
int i ;
for ( i = v - > levels - 2 ; i > = 0 ; i - - ) {
sector_t hash_block_start ;
sector_t hash_block_end ;
2023-02-02 01:42:29 +03:00
2013-03-20 21:21:25 +04:00
verity_hash_at_level ( v , pw - > block , i , & hash_block_start , NULL ) ;
verity_hash_at_level ( v , pw - > block + pw - > n_blocks - 1 , i , & hash_block_end , NULL ) ;
2023-02-02 01:42:29 +03:00
2012-03-28 21:43:38 +04:00
if ( ! i ) {
2023-01-25 23:14:58 +03:00
unsigned int cluster = READ_ONCE ( dm_verity_prefetch_cluster ) ;
2012-03-28 21:43:38 +04:00
cluster > > = v - > data_dev_block_bits ;
if ( unlikely ( ! cluster ) )
goto no_prefetch_cluster ;
if ( unlikely ( cluster & ( cluster - 1 ) ) )
2013-07-11 02:41:17 +04:00
cluster = 1 < < __fls ( cluster ) ;
2012-03-28 21:43:38 +04:00
hash_block_start & = ~ ( sector_t ) ( cluster - 1 ) ;
hash_block_end | = cluster - 1 ;
if ( unlikely ( hash_block_end > = v - > hash_blocks ) )
hash_block_end = v - > hash_blocks - 1 ;
}
no_prefetch_cluster :
dm_bufio_prefetch ( v - > bufio , hash_block_start ,
hash_block_end - hash_block_start + 1 ) ;
}
2013-03-20 21:21:25 +04:00
kfree ( pw ) ;
}
static void verity_submit_prefetch ( struct dm_verity * v , struct dm_verity_io * io )
{
2020-01-07 07:34:24 +03:00
sector_t block = io - > block ;
unsigned int n_blocks = io - > n_blocks ;
2013-03-20 21:21:25 +04:00
struct dm_verity_prefetch_work * pw ;
2020-01-07 07:34:24 +03:00
if ( v - > validated_blocks ) {
while ( n_blocks & & test_bit ( block , v - > validated_blocks ) ) {
block + + ;
n_blocks - - ;
}
while ( n_blocks & & test_bit ( block + n_blocks - 1 ,
v - > validated_blocks ) )
n_blocks - - ;
if ( ! n_blocks )
return ;
}
2013-03-20 21:21:25 +04:00
pw = kmalloc ( sizeof ( struct dm_verity_prefetch_work ) ,
GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN ) ;
if ( ! pw )
return ;
INIT_WORK ( & pw - > work , verity_prefetch_io ) ;
pw - > v = v ;
2020-01-07 07:34:24 +03:00
pw - > block = block ;
pw - > n_blocks = n_blocks ;
2013-03-20 21:21:25 +04:00
queue_work ( v - > verify_wq , & pw - > work ) ;
2012-03-28 21:43:38 +04:00
}
/*
* Bio map function . It allocates dm_verity_io structure and bio vector and
* fills them . Then it issues prefetches and the I / O .
*/
2012-12-22 00:23:41 +04:00
static int verity_map ( struct dm_target * ti , struct bio * bio )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = ti - > private ;
struct dm_verity_io * io ;
2017-08-23 20:10:32 +03:00
bio_set_dev ( bio , v - > data_dev - > bdev ) ;
2013-10-12 02:44:27 +04:00
bio - > bi_iter . bi_sector = verity_map_sector ( v , bio - > bi_iter . bi_sector ) ;
2012-03-28 21:43:38 +04:00
2023-01-25 23:14:58 +03:00
if ( ( ( unsigned int ) bio - > bi_iter . bi_sector | bio_sectors ( bio ) ) &
2012-03-28 21:43:38 +04:00
( ( 1 < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) ) - 1 ) ) {
DMERR_LIMIT ( " unaligned io " ) ;
2017-06-03 10:38:02 +03:00
return DM_MAPIO_KILL ;
2012-03-28 21:43:38 +04:00
}
2012-09-26 02:05:12 +04:00
if ( bio_end_sector ( bio ) > >
2012-03-28 21:43:38 +04:00
( v - > data_dev_block_bits - SECTOR_SHIFT ) > v - > data_blocks ) {
DMERR_LIMIT ( " io out of range " ) ;
2017-06-03 10:38:02 +03:00
return DM_MAPIO_KILL ;
2012-03-28 21:43:38 +04:00
}
if ( bio_data_dir ( bio ) = = WRITE )
2017-06-03 10:38:02 +03:00
return DM_MAPIO_KILL ;
2012-03-28 21:43:38 +04:00
2016-01-31 21:28:26 +03:00
io = dm_per_bio_data ( bio , ti - > per_io_data_size ) ;
2012-03-28 21:43:38 +04:00
io - > v = v ;
io - > orig_bi_end_io = bio - > bi_end_io ;
2013-10-12 02:44:27 +04:00
io - > block = bio - > bi_iter . bi_sector > > ( v - > data_dev_block_bits - SECTOR_SHIFT ) ;
io - > n_blocks = bio - > bi_iter . bi_size > > v - > data_dev_block_bits ;
2012-03-28 21:43:38 +04:00
bio - > bi_end_io = verity_end_io ;
bio - > bi_private = io ;
2013-10-12 02:45:43 +04:00
io - > iter = bio - > bi_iter ;
2012-03-28 21:43:38 +04:00
2013-03-20 21:21:25 +04:00
verity_submit_prefetch ( v , io ) ;
2012-03-28 21:43:38 +04:00
2020-07-01 11:59:44 +03:00
submit_bio_noacct ( bio ) ;
2012-03-28 21:43:38 +04:00
return DM_MAPIO_SUBMITTED ;
}
/*
* Status : V ( valid ) or C ( corruption found )
*/
2013-03-02 02:45:44 +04:00
static void verity_status ( struct dm_target * ti , status_type_t type ,
2023-01-25 23:14:58 +03:00
unsigned int status_flags , char * result , unsigned int maxlen )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = ti - > private ;
2023-01-25 23:14:58 +03:00
unsigned int args = 0 ;
unsigned int sz = 0 ;
unsigned int x ;
2012-03-28 21:43:38 +04:00
switch ( type ) {
case STATUSTYPE_INFO :
DMEMIT ( " %c " , v - > hash_failed ? ' C ' : ' V ' ) ;
break ;
case STATUSTYPE_TABLE :
DMEMIT ( " %u %s %s %u %u %llu %llu %s " ,
v - > version ,
v - > data_dev - > name ,
v - > hash_dev - > name ,
1 < < v - > data_dev_block_bits ,
1 < < v - > hash_dev_block_bits ,
( unsigned long long ) v - > data_blocks ,
( unsigned long long ) v - > hash_start ,
v - > alg_name
) ;
for ( x = 0 ; x < v - > digest_size ; x + + )
DMEMIT ( " %02x " , v - > root_digest [ x ] ) ;
DMEMIT ( " " ) ;
if ( ! v - > salt_size )
DMEMIT ( " - " ) ;
else
for ( x = 0 ; x < v - > salt_size ; x + + )
DMEMIT ( " %02x " , v - > salt [ x ] ) ;
2015-12-03 17:26:30 +03:00
if ( v - > mode ! = DM_VERITY_MODE_EIO )
args + + ;
if ( verity_fec_is_enabled ( v ) )
args + = DM_VERITY_OPTS_FEC ;
2015-12-03 17:26:31 +03:00
if ( v - > zero_digest )
args + + ;
2018-03-23 04:18:04 +03:00
if ( v - > validated_blocks )
args + + ;
2022-07-22 12:38:23 +03:00
if ( v - > use_tasklet )
args + + ;
2019-07-18 03:46:15 +03:00
if ( v - > signature_key_desc )
args + = DM_VERITY_ROOT_HASH_VERIFICATION_OPTS ;
2015-12-03 17:26:30 +03:00
if ( ! args )
return ;
DMEMIT ( " %u " , args ) ;
2015-03-18 18:52:14 +03:00
if ( v - > mode ! = DM_VERITY_MODE_EIO ) {
2015-12-03 17:26:30 +03:00
DMEMIT ( " " ) ;
2015-03-18 18:52:14 +03:00
switch ( v - > mode ) {
case DM_VERITY_MODE_LOGGING :
DMEMIT ( DM_VERITY_OPT_LOGGING ) ;
break ;
case DM_VERITY_MODE_RESTART :
DMEMIT ( DM_VERITY_OPT_RESTART ) ;
break ;
2020-06-18 09:56:50 +03:00
case DM_VERITY_MODE_PANIC :
DMEMIT ( DM_VERITY_OPT_PANIC ) ;
break ;
2015-03-18 18:52:14 +03:00
default :
BUG ( ) ;
}
}
2015-12-03 17:26:31 +03:00
if ( v - > zero_digest )
DMEMIT ( " " DM_VERITY_OPT_IGN_ZEROES ) ;
2018-03-23 04:18:04 +03:00
if ( v - > validated_blocks )
DMEMIT ( " " DM_VERITY_OPT_AT_MOST_ONCE ) ;
2022-07-22 12:38:23 +03:00
if ( v - > use_tasklet )
DMEMIT ( " " DM_VERITY_OPT_TASKLET_VERIFY ) ;
2015-12-03 17:26:30 +03:00
sz = verity_fec_status_table ( v , sz , result , maxlen ) ;
2019-07-18 03:46:15 +03:00
if ( v - > signature_key_desc )
DMEMIT ( " " DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY
" %s " , v - > signature_key_desc ) ;
2012-03-28 21:43:38 +04:00
break ;
2021-07-13 03:49:03 +03:00
case STATUSTYPE_IMA :
DMEMIT_TARGET_NAME_VERSION ( ti - > type ) ;
DMEMIT ( " ,hash_failed=%c " , v - > hash_failed ? ' C ' : ' V ' ) ;
DMEMIT ( " ,verity_version=%u " , v - > version ) ;
DMEMIT ( " ,data_device_name=%s " , v - > data_dev - > name ) ;
DMEMIT ( " ,hash_device_name=%s " , v - > hash_dev - > name ) ;
DMEMIT ( " ,verity_algorithm=%s " , v - > alg_name ) ;
DMEMIT ( " ,root_digest= " ) ;
for ( x = 0 ; x < v - > digest_size ; x + + )
DMEMIT ( " %02x " , v - > root_digest [ x ] ) ;
DMEMIT ( " ,salt= " ) ;
if ( ! v - > salt_size )
DMEMIT ( " - " ) ;
else
for ( x = 0 ; x < v - > salt_size ; x + + )
DMEMIT ( " %02x " , v - > salt [ x ] ) ;
DMEMIT ( " ,ignore_zero_blocks=%c " , v - > zero_digest ? ' y ' : ' n ' ) ;
DMEMIT ( " ,check_at_most_once=%c " , v - > validated_blocks ? ' y ' : ' n ' ) ;
2021-08-14 00:38:00 +03:00
if ( v - > signature_key_desc )
DMEMIT ( " ,root_hash_sig_key_desc=%s " , v - > signature_key_desc ) ;
2021-07-13 03:49:03 +03:00
if ( v - > mode ! = DM_VERITY_MODE_EIO ) {
DMEMIT ( " ,verity_mode= " ) ;
switch ( v - > mode ) {
case DM_VERITY_MODE_LOGGING :
DMEMIT ( DM_VERITY_OPT_LOGGING ) ;
break ;
case DM_VERITY_MODE_RESTART :
DMEMIT ( DM_VERITY_OPT_RESTART ) ;
break ;
case DM_VERITY_MODE_PANIC :
DMEMIT ( DM_VERITY_OPT_PANIC ) ;
break ;
default :
DMEMIT ( " invalid " ) ;
}
}
DMEMIT ( " ; " ) ;
break ;
2012-03-28 21:43:38 +04:00
}
}
2018-04-03 23:54:10 +03:00
static int verity_prepare_ioctl ( struct dm_target * ti , struct block_device * * bdev )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = ti - > private ;
2015-10-15 15:10:50 +03:00
* bdev = v - > data_dev - > bdev ;
2012-03-28 21:43:38 +04:00
2021-10-18 13:11:05 +03:00
if ( v - > data_start | | ti - > len ! = bdev_nr_sectors ( v - > data_dev - > bdev ) )
2015-10-15 15:10:50 +03:00
return 1 ;
return 0 ;
2012-03-28 21:43:38 +04:00
}
static int verity_iterate_devices ( struct dm_target * ti ,
iterate_devices_callout_fn fn , void * data )
{
struct dm_verity * v = ti - > private ;
return fn ( ti , v - > data_dev , v - > data_start , ti - > len , data ) ;
}
static void verity_io_hints ( struct dm_target * ti , struct queue_limits * limits )
{
struct dm_verity * v = ti - > private ;
if ( limits - > logical_block_size < 1 < < v - > data_dev_block_bits )
limits - > logical_block_size = 1 < < v - > data_dev_block_bits ;
if ( limits - > physical_block_size < 1 < < v - > data_dev_block_bits )
limits - > physical_block_size = 1 < < v - > data_dev_block_bits ;
blk_limits_io_min ( limits , limits - > logical_block_size ) ;
}
static void verity_dtr ( struct dm_target * ti )
{
struct dm_verity * v = ti - > private ;
if ( v - > verify_wq )
destroy_workqueue ( v - > verify_wq ) ;
if ( v - > bufio )
dm_bufio_client_destroy ( v - > bufio ) ;
2018-03-23 04:18:04 +03:00
kvfree ( v - > validated_blocks ) ;
2012-03-28 21:43:38 +04:00
kfree ( v - > salt ) ;
kfree ( v - > root_digest ) ;
2015-12-03 17:26:31 +03:00
kfree ( v - > zero_digest ) ;
2012-03-28 21:43:38 +04:00
if ( v - > tfm )
2017-02-19 15:46:07 +03:00
crypto_free_ahash ( v - > tfm ) ;
2012-03-28 21:43:38 +04:00
kfree ( v - > alg_name ) ;
if ( v - > hash_dev )
dm_put_device ( ti , v - > hash_dev ) ;
if ( v - > data_dev )
dm_put_device ( ti , v - > data_dev ) ;
2015-12-03 17:26:30 +03:00
verity_fec_dtr ( v ) ;
2019-07-18 03:46:15 +03:00
kfree ( v - > signature_key_desc ) ;
2022-07-25 23:52:17 +03:00
if ( v - > use_tasklet )
static_branch_dec ( & use_tasklet_enabled ) ;
2012-03-28 21:43:38 +04:00
kfree ( v ) ;
}
2018-03-23 04:18:04 +03:00
static int verity_alloc_most_once ( struct dm_verity * v )
{
struct dm_target * ti = v - > ti ;
/* the bitset can only handle INT_MAX blocks */
if ( v - > data_blocks > INT_MAX ) {
ti - > error = " device too large to use check_at_most_once " ;
return - E2BIG ;
}
treewide: kvzalloc() -> kvcalloc()
The kvzalloc() function has a 2-factor argument form, kvcalloc(). This
patch replaces cases of:
kvzalloc(a * b, gfp)
with:
kvcalloc(a * b, gfp)
as well as handling cases of:
kvzalloc(a * b * c, gfp)
with:
kvzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kvcalloc(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kvzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kvzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kvzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kvzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kvzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kvzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kvzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kvzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kvzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kvzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kvzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kvzalloc
+ kvcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kvzalloc
+ kvcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kvzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kvzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kvzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kvzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kvzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kvzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kvzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kvzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kvzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kvzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kvzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kvzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kvzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kvzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kvzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kvzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kvzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kvzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kvzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kvzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kvzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kvzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kvzalloc(C1 * C2 * C3, ...)
|
kvzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kvzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kvzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kvzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kvzalloc(sizeof(THING) * C2, ...)
|
kvzalloc(sizeof(TYPE) * C2, ...)
|
kvzalloc(C1 * C2 * C3, ...)
|
kvzalloc(C1 * C2, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kvzalloc
+ kvcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kvzalloc
+ kvcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kvzalloc
+ kvcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kvzalloc
+ kvcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:04:48 +03:00
v - > validated_blocks = kvcalloc ( BITS_TO_LONGS ( v - > data_blocks ) ,
sizeof ( unsigned long ) ,
GFP_KERNEL ) ;
2018-03-23 04:18:04 +03:00
if ( ! v - > validated_blocks ) {
ti - > error = " failed to allocate bitset for check_at_most_once " ;
return - ENOMEM ;
}
return 0 ;
}
2015-12-03 17:26:31 +03:00
static int verity_alloc_zero_digest ( struct dm_verity * v )
{
int r = - ENOMEM ;
2017-02-19 15:46:07 +03:00
struct ahash_request * req ;
2015-12-03 17:26:31 +03:00
u8 * zero_data ;
v - > zero_digest = kmalloc ( v - > digest_size , GFP_KERNEL ) ;
if ( ! v - > zero_digest )
return r ;
2017-02-19 15:46:07 +03:00
req = kmalloc ( v - > ahash_reqsize , GFP_KERNEL ) ;
2015-12-03 17:26:31 +03:00
2017-02-19 15:46:07 +03:00
if ( ! req )
2015-12-03 17:26:31 +03:00
return r ; /* verity_dtr will free zero_digest */
zero_data = kzalloc ( 1 < < v - > data_dev_block_bits , GFP_KERNEL ) ;
if ( ! zero_data )
goto out ;
2017-02-19 15:46:07 +03:00
r = verity_hash ( v , req , zero_data , 1 < < v - > data_dev_block_bits ,
2015-12-03 17:26:31 +03:00
v - > zero_digest ) ;
out :
2017-02-19 15:46:07 +03:00
kfree ( req ) ;
2015-12-03 17:26:31 +03:00
kfree ( zero_data ) ;
return r ;
}
2021-03-11 15:10:51 +03:00
static inline bool verity_is_verity_mode ( const char * arg_name )
{
return ( ! strcasecmp ( arg_name , DM_VERITY_OPT_LOGGING ) | |
! strcasecmp ( arg_name , DM_VERITY_OPT_RESTART ) | |
! strcasecmp ( arg_name , DM_VERITY_OPT_PANIC ) ) ;
}
static int verity_parse_verity_mode ( struct dm_verity * v , const char * arg_name )
{
if ( v - > mode )
return - EINVAL ;
if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_LOGGING ) )
v - > mode = DM_VERITY_MODE_LOGGING ;
else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_RESTART ) )
v - > mode = DM_VERITY_MODE_RESTART ;
else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_PANIC ) )
v - > mode = DM_VERITY_MODE_PANIC ;
return 0 ;
}
2019-07-18 03:46:15 +03:00
static int verity_parse_opt_args ( struct dm_arg_set * as , struct dm_verity * v ,
2022-07-26 18:29:50 +03:00
struct dm_verity_sig_opts * verify_args ,
bool only_modifier_opts )
2015-11-05 05:02:32 +03:00
{
2022-08-10 01:07:28 +03:00
int r = 0 ;
2023-01-25 23:14:58 +03:00
unsigned int argc ;
2015-11-05 05:02:32 +03:00
struct dm_target * ti = v - > ti ;
const char * arg_name ;
2017-06-22 21:32:45 +03:00
static const struct dm_arg _args [ ] = {
2015-11-05 05:02:32 +03:00
{ 0 , DM_VERITY_OPTS_MAX , " Invalid number of feature args " } ,
} ;
r = dm_read_arg_group ( _args , as , & argc , & ti - > error ) ;
if ( r )
return - EINVAL ;
if ( ! argc )
return 0 ;
do {
arg_name = dm_shift_arg ( as ) ;
argc - - ;
2021-03-11 15:10:51 +03:00
if ( verity_is_verity_mode ( arg_name ) ) {
2022-07-26 18:29:50 +03:00
if ( only_modifier_opts )
continue ;
2021-03-11 15:10:51 +03:00
r = verity_parse_verity_mode ( v , arg_name ) ;
if ( r ) {
ti - > error = " Conflicting error handling parameters " ;
return r ;
}
2020-06-18 09:56:50 +03:00
continue ;
2015-12-03 17:26:31 +03:00
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_IGN_ZEROES ) ) {
2022-07-26 18:29:50 +03:00
if ( only_modifier_opts )
continue ;
2015-12-03 17:26:31 +03:00
r = verity_alloc_zero_digest ( v ) ;
if ( r ) {
ti - > error = " Cannot allocate zero digest " ;
return r ;
}
continue ;
2018-03-23 04:18:04 +03:00
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_AT_MOST_ONCE ) ) {
2022-07-26 18:29:50 +03:00
if ( only_modifier_opts )
continue ;
2018-03-23 04:18:04 +03:00
r = verity_alloc_most_once ( v ) ;
if ( r )
return r ;
continue ;
2022-07-22 12:38:23 +03:00
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_TASKLET_VERIFY ) ) {
v - > use_tasklet = true ;
2022-07-25 23:52:17 +03:00
static_branch_inc ( & use_tasklet_enabled ) ;
2022-07-22 12:38:23 +03:00
continue ;
2015-12-03 17:26:30 +03:00
} else if ( verity_is_fec_opt_arg ( arg_name ) ) {
2022-07-26 18:29:50 +03:00
if ( only_modifier_opts )
continue ;
2015-12-03 17:26:30 +03:00
r = verity_fec_parse_opt_args ( as , v , & argc , arg_name ) ;
if ( r )
return r ;
continue ;
2022-07-22 12:38:23 +03:00
2019-07-18 03:46:15 +03:00
} else if ( verity_verify_is_sig_opt_arg ( arg_name ) ) {
2022-07-26 18:29:50 +03:00
if ( only_modifier_opts )
continue ;
2019-07-18 03:46:15 +03:00
r = verity_verify_sig_parse_opt_args ( as , v ,
verify_args ,
& argc , arg_name ) ;
if ( r )
return r ;
continue ;
2022-08-10 01:07:28 +03:00
} else if ( only_modifier_opts ) {
/*
* Ignore unrecognized opt , could easily be an extra
* argument to an option whose parsing was skipped .
* Normal parsing ( @ only_modifier_opts = false ) will
* properly parse all options ( and their extra args ) .
*/
continue ;
2015-11-05 05:02:32 +03:00
}
2022-08-10 01:07:28 +03:00
DMERR ( " Unrecognized verity feature request: %s " , arg_name ) ;
2015-11-05 05:02:32 +03:00
ti - > error = " Unrecognized verity feature request " ;
return - EINVAL ;
} while ( argc & & ! r ) ;
return r ;
}
2012-03-28 21:43:38 +04:00
/*
* Target parameters :
* < version > The current format is version 1.
* Vsn 0 is compatible with original Chromium OS releases .
* < data device >
* < hash device >
* < data block size >
* < hash block size >
* < the number of data blocks >
* < hash start block >
* < algorithm >
* < digest >
* < salt > Hex string or " - " if no salt .
*/
2023-01-25 23:14:58 +03:00
static int verity_ctr ( struct dm_target * ti , unsigned int argc , char * * argv )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v ;
2019-07-18 03:46:15 +03:00
struct dm_verity_sig_opts verify_args = { 0 } ;
2015-03-18 18:52:14 +03:00
struct dm_arg_set as ;
2015-11-05 05:02:32 +03:00
unsigned int num ;
2012-03-28 21:43:38 +04:00
unsigned long long num_ll ;
int r ;
int i ;
sector_t hash_position ;
char dummy ;
2019-07-18 03:46:15 +03:00
char * root_hash_digest_to_validate ;
2012-03-28 21:43:38 +04:00
v = kzalloc ( sizeof ( struct dm_verity ) , GFP_KERNEL ) ;
if ( ! v ) {
ti - > error = " Cannot allocate verity structure " ;
return - ENOMEM ;
}
ti - > private = v ;
v - > ti = ti ;
2015-12-03 17:26:30 +03:00
r = verity_fec_ctr_alloc ( v ) ;
if ( r )
goto bad ;
2012-03-28 21:43:38 +04:00
if ( ( dm_table_get_mode ( ti - > table ) & ~ FMODE_READ ) ) {
ti - > error = " Device must be readonly " ;
r = - EINVAL ;
goto bad ;
}
2015-03-18 18:52:14 +03:00
if ( argc < 10 ) {
ti - > error = " Not enough arguments " ;
2012-03-28 21:43:38 +04:00
r = - EINVAL ;
goto bad ;
}
2022-07-26 18:29:50 +03:00
/* Parse optional parameters that modify primary args */
if ( argc > 10 ) {
as . argc = argc - 10 ;
as . argv = argv + 10 ;
r = verity_parse_opt_args ( & as , v , & verify_args , true ) ;
if ( r < 0 )
goto bad ;
}
2013-07-11 02:41:17 +04:00
if ( sscanf ( argv [ 0 ] , " %u%c " , & num , & dummy ) ! = 1 | |
num > 1 ) {
2012-03-28 21:43:38 +04:00
ti - > error = " Invalid version " ;
r = - EINVAL ;
goto bad ;
}
v - > version = num ;
r = dm_get_device ( ti , argv [ 1 ] , FMODE_READ , & v - > data_dev ) ;
if ( r ) {
ti - > error = " Data device lookup failed " ;
goto bad ;
}
r = dm_get_device ( ti , argv [ 2 ] , FMODE_READ , & v - > hash_dev ) ;
if ( r ) {
2016-09-08 22:21:28 +03:00
ti - > error = " Hash device lookup failed " ;
2012-03-28 21:43:38 +04:00
goto bad ;
}
if ( sscanf ( argv [ 3 ] , " %u%c " , & num , & dummy ) ! = 1 | |
! num | | ( num & ( num - 1 ) ) | |
num < bdev_logical_block_size ( v - > data_dev - > bdev ) | |
num > PAGE_SIZE ) {
ti - > error = " Invalid data device block size " ;
r = - EINVAL ;
goto bad ;
}
2013-07-11 02:41:17 +04:00
v - > data_dev_block_bits = __ffs ( num ) ;
2012-03-28 21:43:38 +04:00
if ( sscanf ( argv [ 4 ] , " %u%c " , & num , & dummy ) ! = 1 | |
! num | | ( num & ( num - 1 ) ) | |
num < bdev_logical_block_size ( v - > hash_dev - > bdev ) | |
num > INT_MAX ) {
ti - > error = " Invalid hash device block size " ;
r = - EINVAL ;
goto bad ;
}
2013-07-11 02:41:17 +04:00
v - > hash_dev_block_bits = __ffs ( num ) ;
2012-03-28 21:43:38 +04:00
if ( sscanf ( argv [ 5 ] , " %llu%c " , & num_ll , & dummy ) ! = 1 | |
2012-09-27 02:45:48 +04:00
( sector_t ) ( num_ll < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) )
> > ( v - > data_dev_block_bits - SECTOR_SHIFT ) ! = num_ll ) {
2012-03-28 21:43:38 +04:00
ti - > error = " Invalid data blocks " ;
r = - EINVAL ;
goto bad ;
}
v - > data_blocks = num_ll ;
if ( ti - > len > ( v - > data_blocks < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) ) ) {
ti - > error = " Data device is too small " ;
r = - EINVAL ;
goto bad ;
}
if ( sscanf ( argv [ 6 ] , " %llu%c " , & num_ll , & dummy ) ! = 1 | |
2012-09-27 02:45:48 +04:00
( sector_t ) ( num_ll < < ( v - > hash_dev_block_bits - SECTOR_SHIFT ) )
> > ( v - > hash_dev_block_bits - SECTOR_SHIFT ) ! = num_ll ) {
2012-03-28 21:43:38 +04:00
ti - > error = " Invalid hash start " ;
r = - EINVAL ;
goto bad ;
}
v - > hash_start = num_ll ;
v - > alg_name = kstrdup ( argv [ 7 ] , GFP_KERNEL ) ;
if ( ! v - > alg_name ) {
ti - > error = " Cannot allocate algorithm name " ;
r = - ENOMEM ;
goto bad ;
}
2022-07-26 18:29:50 +03:00
v - > tfm = crypto_alloc_ahash ( v - > alg_name , 0 ,
v - > use_tasklet ? CRYPTO_ALG_ASYNC : 0 ) ;
2012-03-28 21:43:38 +04:00
if ( IS_ERR ( v - > tfm ) ) {
ti - > error = " Cannot initialize hash function " ;
r = PTR_ERR ( v - > tfm ) ;
v - > tfm = NULL ;
goto bad ;
}
2018-12-06 07:54:13 +03:00
/*
* dm - verity performance can vary greatly depending on which hash
* algorithm implementation is used . Help people debug performance
* problems by logging the - > cra_driver_name .
*/
DMINFO ( " %s using implementation \" %s \" " , v - > alg_name ,
crypto_hash_alg_common ( v - > tfm ) - > base . cra_driver_name ) ;
2017-02-19 15:46:07 +03:00
v - > digest_size = crypto_ahash_digestsize ( v - > tfm ) ;
2012-03-28 21:43:38 +04:00
if ( ( 1 < < v - > hash_dev_block_bits ) < v - > digest_size * 2 ) {
ti - > error = " Digest size too big " ;
r = - EINVAL ;
goto bad ;
}
2017-02-19 15:46:07 +03:00
v - > ahash_reqsize = sizeof ( struct ahash_request ) +
crypto_ahash_reqsize ( v - > tfm ) ;
2012-03-28 21:43:38 +04:00
v - > root_digest = kmalloc ( v - > digest_size , GFP_KERNEL ) ;
if ( ! v - > root_digest ) {
ti - > error = " Cannot allocate root digest " ;
r = - ENOMEM ;
goto bad ;
}
if ( strlen ( argv [ 8 ] ) ! = v - > digest_size * 2 | |
hex2bin ( v - > root_digest , argv [ 8 ] , v - > digest_size ) ) {
ti - > error = " Invalid root digest " ;
r = - EINVAL ;
goto bad ;
}
2019-07-18 03:46:15 +03:00
root_hash_digest_to_validate = argv [ 8 ] ;
2012-03-28 21:43:38 +04:00
if ( strcmp ( argv [ 9 ] , " - " ) ) {
v - > salt_size = strlen ( argv [ 9 ] ) / 2 ;
v - > salt = kmalloc ( v - > salt_size , GFP_KERNEL ) ;
if ( ! v - > salt ) {
ti - > error = " Cannot allocate salt " ;
r = - ENOMEM ;
goto bad ;
}
if ( strlen ( argv [ 9 ] ) ! = v - > salt_size * 2 | |
hex2bin ( v - > salt , argv [ 9 ] , v - > salt_size ) ) {
ti - > error = " Invalid salt " ;
r = - EINVAL ;
goto bad ;
}
}
2015-03-18 18:52:14 +03:00
argv + = 10 ;
argc - = 10 ;
/* Optional parameters */
if ( argc ) {
as . argc = argc ;
as . argv = argv ;
2022-07-26 18:29:50 +03:00
r = verity_parse_opt_args ( & as , v , & verify_args , false ) ;
2015-11-05 05:02:32 +03:00
if ( r < 0 )
2015-03-18 18:52:14 +03:00
goto bad ;
}
2019-07-18 03:46:15 +03:00
/* Root hash signature is a optional parameter*/
r = verity_verify_root_hash ( root_hash_digest_to_validate ,
strlen ( root_hash_digest_to_validate ) ,
verify_args . sig ,
verify_args . sig_size ) ;
if ( r < 0 ) {
ti - > error = " Root hash verification failed " ;
goto bad ;
}
2012-03-28 21:43:38 +04:00
v - > hash_per_block_bits =
2013-07-11 02:41:17 +04:00
__fls ( ( 1 < < v - > hash_dev_block_bits ) / v - > digest_size ) ;
2012-03-28 21:43:38 +04:00
v - > levels = 0 ;
if ( v - > data_blocks )
while ( v - > hash_per_block_bits * v - > levels < 64 & &
( unsigned long long ) ( v - > data_blocks - 1 ) > >
( v - > hash_per_block_bits * v - > levels ) )
v - > levels + + ;
if ( v - > levels > DM_VERITY_MAX_LEVELS ) {
ti - > error = " Too many tree levels " ;
r = - E2BIG ;
goto bad ;
}
hash_position = v - > hash_start ;
for ( i = v - > levels - 1 ; i > = 0 ; i - - ) {
sector_t s ;
2023-02-02 01:42:29 +03:00
2012-03-28 21:43:38 +04:00
v - > hash_level_block [ i ] = hash_position ;
2013-07-11 02:41:16 +04:00
s = ( v - > data_blocks + ( ( sector_t ) 1 < < ( ( i + 1 ) * v - > hash_per_block_bits ) ) - 1 )
> > ( ( i + 1 ) * v - > hash_per_block_bits ) ;
2012-03-28 21:43:38 +04:00
if ( hash_position + s < hash_position ) {
ti - > error = " Hash device offset overflow " ;
r = - E2BIG ;
goto bad ;
}
hash_position + = s ;
}
v - > hash_blocks = hash_position ;
v - > bufio = dm_bufio_client_create ( v - > hash_dev - > bdev ,
1 < < v - > hash_dev_block_bits , 1 , sizeof ( struct buffer_aux ) ,
2022-07-22 12:38:23 +03:00
dm_bufio_alloc_callback , NULL ,
v - > use_tasklet ? DM_BUFIO_CLIENT_NO_SLEEP : 0 ) ;
2012-03-28 21:43:38 +04:00
if ( IS_ERR ( v - > bufio ) ) {
ti - > error = " Cannot initialize dm-bufio " ;
r = PTR_ERR ( v - > bufio ) ;
v - > bufio = NULL ;
goto bad ;
}
if ( dm_bufio_get_device_size ( v - > bufio ) < v - > hash_blocks ) {
ti - > error = " Hash device is too small " ;
r = - E2BIG ;
goto bad ;
}
dm verity: enable WQ_HIGHPRI on verify_wq
WQ_HIGHPRI increases throughput and decreases disk latency when using
dm-verity. This is important in Android for camera startup speed.
The following tests were run by doing 60 seconds of random reads using
a dm-verity device backed by two ramdisks.
Without WQ_HIGHPRI
lat (usec): min=13, max=3947, avg=69.53, stdev=50.55
READ: bw=51.1MiB/s (53.6MB/s), 51.1MiB/s-51.1MiB/s (53.6MB/s-53.6MB/s)
With WQ_HIGHPRI:
lat (usec): min=13, max=7854, avg=31.15, stdev=30.42
READ: bw=116MiB/s (121MB/s), 116MiB/s-116MiB/s (121MB/s-121MB/s)
Further testing was done by measuring how long it takes to open a
camera on an Android device.
Without WQ_HIGHPRI
Total verity work queue wait times (ms):
880.960, 789.517, 898.852
With WQ_HIGHPRI:
Total verity work queue wait times (ms):
528.824, 439.191, 433.300
The average time to open the camera is reduced by 350ms (or 40-50%).
Signed-off-by: Nathan Huckleberry <nhuck@google.com>
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
2022-08-30 21:44:44 +03:00
/*
* Using WQ_HIGHPRI improves throughput and completion latency by
* reducing wait times when reading from a dm - verity device .
*
* Also as required for the " try_verify_in_tasklet " feature : WQ_HIGHPRI
* allows verify_wq to preempt softirq since verification in tasklet
* will fall - back to using it for error handling ( or if the bufio cache
* doesn ' t have required hashes ) .
*/
2023-02-02 04:23:48 +03:00
v - > verify_wq = alloc_workqueue ( " kverityd " , WQ_MEM_RECLAIM | WQ_HIGHPRI , 0 ) ;
2012-03-28 21:43:38 +04:00
if ( ! v - > verify_wq ) {
ti - > error = " Cannot allocate workqueue " ;
r = - ENOMEM ;
goto bad ;
}
2016-01-31 21:28:26 +03:00
ti - > per_io_data_size = sizeof ( struct dm_verity_io ) +
2017-02-19 15:46:07 +03:00
v - > ahash_reqsize + v - > digest_size * 2 ;
2015-12-03 17:26:30 +03:00
r = verity_fec_ctr ( v ) ;
if ( r )
goto bad ;
2016-01-31 21:28:26 +03:00
ti - > per_io_data_size = roundup ( ti - > per_io_data_size ,
__alignof__ ( struct dm_verity_io ) ) ;
2015-12-03 17:26:30 +03:00
2019-07-18 03:46:15 +03:00
verity_verify_sig_opts_cleanup ( & verify_args ) ;
2012-03-28 21:43:38 +04:00
return 0 ;
bad :
2019-07-18 03:46:15 +03:00
verity_verify_sig_opts_cleanup ( & verify_args ) ;
2012-03-28 21:43:38 +04:00
verity_dtr ( ti ) ;
return r ;
}
2022-06-27 18:35:24 +03:00
/*
* Check whether a DM target is a verity target .
*/
bool dm_is_verity_target ( struct dm_target * ti )
{
return ti - > type - > module = = THIS_MODULE ;
}
2022-09-07 23:30:58 +03:00
/*
* Get the verity mode ( error behavior ) of a verity target .
*
* Returns the verity mode of the target , or - EINVAL if ' ti ' is not a verity
* target .
*/
int dm_verity_get_mode ( struct dm_target * ti )
{
struct dm_verity * v = ti - > private ;
if ( ! dm_is_verity_target ( ti ) )
return - EINVAL ;
return v - > mode ;
}
2022-06-27 18:35:24 +03:00
/*
* Get the root digest of a verity target .
*
* Returns a copy of the root digest , the caller is responsible for
* freeing the memory of the digest .
*/
int dm_verity_get_root_digest ( struct dm_target * ti , u8 * * root_digest , unsigned int * digest_size )
{
struct dm_verity * v = ti - > private ;
if ( ! dm_is_verity_target ( ti ) )
return - EINVAL ;
* root_digest = kmemdup ( v - > root_digest , v - > digest_size , GFP_KERNEL ) ;
if ( * root_digest = = NULL )
return - ENOMEM ;
* digest_size = v - > digest_size ;
return 0 ;
}
2012-03-28 21:43:38 +04:00
static struct target_type verity_target = {
. name = " verity " ,
2022-05-31 22:56:40 +03:00
. features = DM_TARGET_IMMUTABLE ,
2022-07-22 12:38:23 +03:00
. version = { 1 , 9 , 0 } ,
2012-03-28 21:43:38 +04:00
. module = THIS_MODULE ,
. ctr = verity_ctr ,
. dtr = verity_dtr ,
. map = verity_map ,
. status = verity_status ,
2015-10-15 15:10:50 +03:00
. prepare_ioctl = verity_prepare_ioctl ,
2012-03-28 21:43:38 +04:00
. iterate_devices = verity_iterate_devices ,
. io_hints = verity_io_hints ,
} ;
static int __init dm_verity_init ( void )
{
int r ;
r = dm_register_target ( & verity_target ) ;
if ( r < 0 )
DMERR ( " register failed %d " , r ) ;
return r ;
}
static void __exit dm_verity_exit ( void )
{
dm_unregister_target ( & verity_target ) ;
}
module_init ( dm_verity_init ) ;
module_exit ( dm_verity_exit ) ;
MODULE_AUTHOR ( " Mikulas Patocka <mpatocka@redhat.com> " ) ;
MODULE_AUTHOR ( " Mandeep Baines <msb@chromium.org> " ) ;
MODULE_AUTHOR ( " Will Drewry <wad@chromium.org> " ) ;
MODULE_DESCRIPTION ( DM_NAME " target for transparent disk integrity checking " ) ;
MODULE_LICENSE ( " GPL " ) ;