linux-fscrypt.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers@kernel.org>
To: Boris Burkov <boris@bur.io>
Cc: linux-btrfs@vger.kernel.org, kernel-team@fb.com,
	linux-fscrypt@vger.kernel.org
Subject: Re: [PATCH v2 2/5] btrfs: initial fsverity support
Date: Mon, 15 Mar 2021 16:17:17 -0700	[thread overview]
Message-ID: <YE/q/bYckkAewbbl@gmail.com> (raw)
In-Reply-To: <71249018efc661fdd3c43bda5d7cea271904ae1a.1614971203.git.boris@bur.io>

On Fri, Mar 05, 2021 at 11:26:30AM -0800, Boris Burkov wrote:
> +static int end_page_read(struct page *page, bool uptodate, u64 start, u32 len)
>  {
> -	struct btrfs_fs_info *fs_info = btrfs_sb(page->mapping->host->i_sb);
> +	int ret = 0;
> +	struct inode *inode = page->mapping->host;
> +	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
>  
>  	ASSERT(page_offset(page) <= start &&
>  		start + len <= page_offset(page) + PAGE_SIZE);
>  
>  	if (uptodate) {
> -		btrfs_page_set_uptodate(fs_info, page, start, len);
> +		/*
> +		 * buffered reads of a file with page alignment will issue a
> +		 * 0 length read for one page past the end of file, so we must
> +		 * explicitly skip checking verity on that page of zeros.
> +		 */
> +		if (!PageError(page) && !PageUptodate(page) &&
> +		    start < i_size_read(inode) &&
> +		    fsverity_active(inode) &&
> +		    fsverity_verify_page(page) != true)
> +			ret = -EIO;
> +		else
> +			btrfs_page_set_uptodate(fs_info, page, start, len);
>  	} else {

'fsverity_verify_page(page) != true' is better written as
'!fsverity_verify_page(page)'.

> +/*
> + * Just like ext4, we cache the merkle tree in pages after EOF in the page
> + * cache.  Unlike ext4, we're storing these in dedicated btree items and
> + * not just shoving them after EOF in the file.  This means we'll need to
> + * do extra work to encrypt them once encryption is supported in btrfs,
> + * but btrfs has a lot of careful code around i_size and it seems better
> + * to make a new key type than try and adjust all of our expectations
> + * for i_size.
> + *
> + * fs verity items are stored under two different key types on disk.
> + *
> + * The descriptor items:
> + * [ inode objectid, BTRFS_VERITY_DESC_ITEM_KEY, offset ]
> + *
> + * At offset 0, we store a btrfs_verity_descriptor_item which tracks the
> + * size of the descriptor item and some extra data for encryption.

Is the separate size field really needed?  It seems like the type of thing that
would be redundant with information that btrfs already stores about the tree
items.  Having two sources of truth is error-prone.

> +/*
> + * Insert and write inode items with a given key type and offset.
> + * @inode: The inode to insert for.
> + * @key_type: The key type to insert.
> + * @offset: The item offset to insert at.
> + * @src: Source data to write.
> + * @len: Length of source data to write.
> + * @max_item_size: Break up the write into items of this size at most.
> + *                 Useful for small leaf size file systems.
> + *
> + * Write len bytes from src into items of up to max_item_size length.
> + * The inserted items will have key <ino, key_type, offset + off> where
> + * off is consecutively increasing from 0 up to the last item ending at
> + * offset + len.
> + *
> + * Returns 0 on success and a negative error code on failure.
> + */
> +static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
> +			   const char *src, u64 len)

The max_item_size parameter is documented but doesn't exist.

> +/*
> + * fsverity op that gets the struct fsverity_descriptor.
> + * fsverity does a two pass setup for reading the descriptor, in the first pass
> + * it calls with buf_size = 0 to query the size of the descriptor,
> + * and then in the second pass it actually reads the descriptor off
> + * disk.
> + */
> +static int btrfs_get_verity_descriptor(struct inode *inode, void *buf,
> +				       size_t buf_size)
> +{
> +	size_t true_size;
> +	ssize_t ret = 0;
> +	struct btrfs_verity_descriptor_item item;
> +
> +	memset(&item, 0, sizeof(item));
> +	ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_DESC_ITEM_KEY,
> +			     0, (char *)&item, sizeof(item), NULL);
> +	if (ret < 0)
> +		return ret;
> +
> +	true_size = btrfs_stack_verity_descriptor_size(&item);
> +	if (!buf_size)
> +		return true_size;
> +	if (buf_size < true_size)
> +		return -ERANGE;


It would be good to validate that true_size <= INT_MAX, as it's returned it an
'int'.

> +
> +	ret = read_key_bytes(BTRFS_I(inode),
> +			     BTRFS_VERITY_DESC_ITEM_KEY, 1,
> +			     buf, buf_size, NULL);
> +	if (ret < 0)
> +		return ret;
> +	if (ret != buf_size)
> +		return -EIO;
> +
> +	return buf_size;

Shouldn't this part use true_size, not buf_size?

> +/*
> + * fsverity op that writes a merkle tree block into the btree in 1k chunks.
> + */
> +static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf,
> +					u64 index, int log_blocksize)
> +{
> +	u64 start = index << log_blocksize;
> +	u64 len = 1 << log_blocksize;
> +	unsigned long mapping_index = get_verity_mapping_index(inode, index);
> +
> +	if (mapping_index > inode->i_sb->s_maxbytes >> PAGE_SHIFT)
> +		return -EFBIG;

I don't think this overflow check will work correctly, as 'mapping_index' can
overflow a ULONG_MAX before the overflow check is done.

> +struct btrfs_verity_descriptor_item {
> +	/* size of the verity descriptor in bytes */
> +	__le64 size;
> +	__le64 reserved[2];
> +	__u8 encryption;
> +} __attribute__ ((__packed__));

Should these reserved fields be strictly validated?  Currently they appear to be
ignored.

- Eric

  parent reply	other threads:[~2021-03-15 23:17 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-05 19:26 [PATCH v2 0/5] btrfs: support fsverity Boris Burkov
2021-03-05 19:26 ` [PATCH v2 1/5] btrfs: add compat_flags to btrfs_inode_item Boris Burkov
2021-03-15 23:07   ` Eric Biggers
2021-03-15 23:29     ` Boris Burkov
2021-03-05 19:26 ` [PATCH v2 2/5] btrfs: initial fsverity support Boris Burkov
2021-03-07  7:13   ` kernel test robot
2021-03-15 23:17   ` Eric Biggers [this message]
2021-03-16  0:42     ` Boris Burkov
2021-03-16  0:57       ` Eric Biggers
2021-03-16 18:44   ` Eric Biggers
2021-03-05 19:26 ` [PATCH v2 3/5] btrfs: check verity for reads of inline extents and holes Boris Burkov
2021-03-05 19:26 ` [PATCH v2 4/5] btrfs: fallback to buffered io for verity files Boris Burkov
2021-03-05 19:26 ` [PATCH v2 5/5] btrfs: verity metadata orphan items Boris Burkov
2021-03-15 23:09 ` [PATCH v2 0/5] btrfs: support fsverity Eric Biggers
2021-03-15 23:47   ` Boris Burkov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YE/q/bYckkAewbbl@gmail.com \
    --to=ebiggers@kernel.org \
    --cc=boris@bur.io \
    --cc=kernel-team@fb.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=linux-fscrypt@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).