ANDROID: fscrypt: fix DUN contiguity with inline encryption + IV_INO_LBLK_32 policies

IV_INO_LBLK_32 policies introduced the possibility that logically
contiguous data blocks might not have contiguous DUNs (because of
potential DUN wraparound). As such, whenever a page is merged into a
bio, fscrypt_mergeable_bio() must be called to check DUN contiguity.
Fix fscrypt_zeroout_range_inline_crypt by calling
fscrypt_mergeable_bio() before each page merge.

Further, fscrypt inline encryption does not handle the case when the DUN
wraps around within a page (which can happen when the data unit size !=
PAGE_SIZE). For now, we handle that by disallowing inline encryption
with IV_INO_LBLK_32 policies when the data unit size != PAGE_SIZE (and
dropping the now redundant check for this in fscrypt_dio_supported()).

Fixes: c2b86b727a ("FROMLIST: Update Inline Encryption from v6 to upstream version of patch series")
Change-Id: I9cb414fcc284b197b9d3d1b9643029c6b875df5a
Signed-off-by: Satya Tangirala <satyat@google.com>
This commit is contained in:
Satya Tangirala 2020-06-29 23:17:28 -07:00 committed by Eric Biggers
parent b4518aa55c
commit a79db694f3
2 changed files with 19 additions and 13 deletions

View File

@ -74,7 +74,8 @@ static int fscrypt_zeroout_range_inline_crypt(const struct inode *inode,
len -= blocks_this_page;
lblk += blocks_this_page;
pblk += blocks_this_page;
if (num_pages == BIO_MAX_PAGES || !len) {
if (num_pages == BIO_MAX_PAGES || !len ||
!fscrypt_mergeable_bio(bio, inode, lblk)) {
err = submit_bio_wait(bio);
if (err)
goto out;

View File

@ -87,6 +87,19 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci,
if (!(sb->s_flags & SB_INLINECRYPT))
return 0;
/*
* When a page contains multiple logically contiguous filesystem blocks,
* some filesystem code only calls fscrypt_mergeable_bio() for the first
* block in the page. This is fine for most of fscrypt's IV generation
* strategies, where contiguous blocks imply contiguous IVs. But it
* doesn't work with IV_INO_LBLK_32. For now, simply exclude
* IV_INO_LBLK_32 with blocksize != PAGE_SIZE from inline encryption.
*/
if ((fscrypt_policy_flags(&ci->ci_policy) &
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) &&
sb->s_blocksize != PAGE_SIZE)
return 0;
/*
* blk-crypto must support the crypto configuration we'll use for the
* inode on all devices in the sb
@ -397,7 +410,6 @@ EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio_bh);
bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter)
{
const struct inode *inode = file_inode(iocb->ki_filp);
const struct fscrypt_info *ci = inode->i_crypt_info;
const unsigned int blocksize = i_blocksize(inode);
/* If the file is unencrypted, no veto from us. */
@ -415,15 +427,6 @@ bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter)
if (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter), blocksize))
return false;
/*
* With IV_INO_LBLK_32 and sub-page blocks, the DUN can wrap around in
* the middle of a page. This isn't handled by the direct I/O code yet.
*/
if (blocksize != PAGE_SIZE &&
(fscrypt_policy_flags(&ci->ci_policy) &
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
return false;
return true;
}
EXPORT_SYMBOL_GPL(fscrypt_dio_supported);
@ -438,8 +441,6 @@ EXPORT_SYMBOL_GPL(fscrypt_dio_supported);
* targeting @pos, in order to avoid crossing a data unit number (DUN)
* discontinuity. This is only needed for certain IV generation methods.
*
* This assumes block_size == PAGE_SIZE; see fscrypt_dio_supported().
*
* Return: the actual number of pages that can be submitted
*/
int fscrypt_limit_dio_pages(const struct inode *inode, loff_t pos, int nr_pages)
@ -457,6 +458,10 @@ int fscrypt_limit_dio_pages(const struct inode *inode, loff_t pos, int nr_pages)
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
return nr_pages;
/*
* fscrypt_select_encryption_impl() ensures that block_size == PAGE_SIZE
* when using FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32.
*/
if (WARN_ON_ONCE(i_blocksize(inode) != PAGE_SIZE))
return 1;