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.

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()).

Bug: 144046242
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 cfed4cb19a
commit 6561560c01
2 changed files with 19 additions and 13 deletions

View File

@ -77,7 +77,8 @@ static int fscrypt_zeroout_range_inlinecrypt(const struct inode *inode,
lblk += blocks_this_page;
pblk += blocks_this_page;
len -= blocks_this_page;
} while (++i != BIO_MAX_PAGES && len != 0);
} while (++i != BIO_MAX_PAGES && len != 0 &&
fscrypt_mergeable_bio(bio, inode, lblk));
err = submit_bio_wait(bio);
if (err)

View File

@ -88,6 +88,19 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci,
!sb->s_cop->inline_crypt_enabled(sb))
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;
/*
* The needed encryption settings must be supported either by
* blk-crypto-fallback, or by hardware on all the filesystem's devices.
@ -441,7 +454,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. */
@ -459,15 +471,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);
@ -482,8 +485,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)
@ -501,6 +502,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;