diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-26 08:51:51 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-26 08:51:51 -0700 |
commit | c3558a6b2a75d9adacf15dd7fae79dbfffa7ebe4 (patch) | |
tree | 54988c4e9aa437a845f3fb0def3c1ccbe9360ee3 | |
parent | dbe0e78d0e3a83dd924ea01bebf6c45313c81607 (diff) | |
parent | 04839139213cf60d4c5fc792214a08830e294ff8 (diff) | |
download | lwn-c3558a6b2a75d9adacf15dd7fae79dbfffa7ebe4.tar.gz lwn-c3558a6b2a75d9adacf15dd7fae79dbfffa7ebe4.zip |
Merge tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux
Pull fsverity updates from Eric Biggers:
"Several cleanups and fixes for fs/verity/, including a couple minor
fixes to the changes in 6.3 that added support for Merkle tree block
sizes less than the page size"
* tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux:
fsverity: reject FS_IOC_ENABLE_VERITY on mode 3 fds
fsverity: explicitly check for buffer overflow in build_merkle_tree()
fsverity: use WARN_ON_ONCE instead of WARN_ON
fs-verity: simplify sysctls with register_sysctl()
fs/buffer.c: use b_folio for fsverity work
-rw-r--r-- | fs/buffer.c | 9 | ||||
-rw-r--r-- | fs/verity/enable.c | 21 | ||||
-rw-r--r-- | fs/verity/hash_algs.c | 4 | ||||
-rw-r--r-- | fs/verity/open.c | 2 | ||||
-rw-r--r-- | fs/verity/signature.c | 9 | ||||
-rw-r--r-- | include/linux/fsverity.h | 6 |
6 files changed, 30 insertions, 21 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index 737e3eff6259..b3eb905f87d6 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -308,20 +308,19 @@ static void verify_bh(struct work_struct *work) struct buffer_head *bh = ctx->bh; bool valid; - valid = fsverity_verify_blocks(page_folio(bh->b_page), bh->b_size, - bh_offset(bh)); + valid = fsverity_verify_blocks(bh->b_folio, bh->b_size, bh_offset(bh)); end_buffer_async_read(bh, valid); kfree(ctx); } static bool need_fsverity(struct buffer_head *bh) { - struct page *page = bh->b_page; - struct inode *inode = page->mapping->host; + struct folio *folio = bh->b_folio; + struct inode *inode = folio->mapping->host; return fsverity_active(inode) && /* needed by ext4 */ - page->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE); + folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE); } static void decrypt_bh(struct work_struct *work) diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 7a0e3a84d370..fc4c50e5219d 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -13,6 +13,7 @@ struct block_buffer { u32 filled; + bool is_root_hash; u8 *data; }; @@ -24,6 +25,14 @@ static int hash_one_block(struct inode *inode, struct block_buffer *next = cur + 1; int err; + /* + * Safety check to prevent a buffer overflow in case of a filesystem bug + * that allows the file size to change despite deny_write_access(), or a + * bug in the Merkle tree logic itself + */ + if (WARN_ON_ONCE(next->is_root_hash && next->filled != 0)) + return -EINVAL; + /* Zero-pad the block if it's shorter than the block size. */ memset(&cur->data[cur->filled], 0, params->block_size - cur->filled); @@ -97,6 +106,7 @@ static int build_merkle_tree(struct file *filp, } } buffers[num_levels].data = root_hash; + buffers[num_levels].is_root_hash = true; BUILD_BUG_ON(sizeof(level_offset) != sizeof(params->level_start)); memcpy(level_offset, params->level_start, sizeof(level_offset)); @@ -165,7 +175,7 @@ static int build_merkle_tree(struct file *filp, } } /* The root hash was filled by the last call to hash_one_block(). */ - if (WARN_ON(buffers[num_levels].filled != params->digest_size)) { + if (WARN_ON_ONCE(buffers[num_levels].filled != params->digest_size)) { err = -EINVAL; goto out; } @@ -277,7 +287,7 @@ static int enable_verity(struct file *filp, fsverity_err(inode, "%ps() failed with err %d", vops->end_enable_verity, err); fsverity_free_info(vi); - } else if (WARN_ON(!IS_VERITY(inode))) { + } else if (WARN_ON_ONCE(!IS_VERITY(inode))) { err = -EINVAL; fsverity_free_info(vi); } else { @@ -347,6 +357,13 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) err = file_permission(filp, MAY_WRITE); if (err) return err; + /* + * __kernel_read() is used while building the Merkle tree. So, we can't + * allow file descriptors that were opened for ioctl access only, using + * the special nonstandard access mode 3. O_RDONLY only, please! + */ + if (!(filp->f_mode & FMODE_READ)) + return -EBADF; if (IS_APPEND(inode)) return -EPERM; diff --git a/fs/verity/hash_algs.c b/fs/verity/hash_algs.c index 13fcf31be844..ea00dbedf756 100644 --- a/fs/verity/hash_algs.c +++ b/fs/verity/hash_algs.c @@ -84,9 +84,9 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, } err = -EINVAL; - if (WARN_ON(alg->digest_size != crypto_ahash_digestsize(tfm))) + if (WARN_ON_ONCE(alg->digest_size != crypto_ahash_digestsize(tfm))) goto err_free_tfm; - if (WARN_ON(alg->block_size != crypto_ahash_blocksize(tfm))) + if (WARN_ON_ONCE(alg->block_size != crypto_ahash_blocksize(tfm))) goto err_free_tfm; err = mempool_init_kmalloc_pool(&alg->req_pool, 1, diff --git a/fs/verity/open.c b/fs/verity/open.c index 9366b441d01c..52048b7630dc 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -83,7 +83,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, params->log_blocks_per_page = PAGE_SHIFT - log_blocksize; params->blocks_per_page = 1 << params->log_blocks_per_page; - if (WARN_ON(!is_power_of_2(params->digest_size))) { + if (WARN_ON_ONCE(!is_power_of_2(params->digest_size))) { err = -EINVAL; goto out_err; } diff --git a/fs/verity/signature.c b/fs/verity/signature.c index e7d3ca919a1e..b8c51ad40d3a 100644 --- a/fs/verity/signature.c +++ b/fs/verity/signature.c @@ -88,12 +88,6 @@ int fsverity_verify_signature(const struct fsverity_info *vi, #ifdef CONFIG_SYSCTL static struct ctl_table_header *fsverity_sysctl_header; -static const struct ctl_path fsverity_sysctl_path[] = { - { .procname = "fs", }, - { .procname = "verity", }, - { } -}; - static struct ctl_table fsverity_sysctl_table[] = { { .procname = "require_signatures", @@ -109,8 +103,7 @@ static struct ctl_table fsverity_sysctl_table[] = { static int __init fsverity_sysctl_init(void) { - fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path, - fsverity_sysctl_table); + fsverity_sysctl_header = register_sysctl("fs/verity", fsverity_sysctl_table); if (!fsverity_sysctl_header) { pr_err("sysctl registration failed!\n"); return -ENOMEM; diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 119a3266791f..e76605d5b36e 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -233,18 +233,18 @@ static inline int fsverity_ioctl_read_metadata(struct file *filp, static inline bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset) { - WARN_ON(1); + WARN_ON_ONCE(1); return false; } static inline void fsverity_verify_bio(struct bio *bio) { - WARN_ON(1); + WARN_ON_ONCE(1); } static inline void fsverity_enqueue_verify_work(struct work_struct *work) { - WARN_ON(1); + WARN_ON_ONCE(1); } #endif /* !CONFIG_FS_VERITY */ |