From patchwork Sat Mar 30 00:32:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611284 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DEECF7E2; Sat, 30 Mar 2024 00:32:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758777; cv=none; b=g09B+w7369hNRhrrDEtkYaQXRHIQZdAkz5QNtq9CfKuQDqKidCORyRD5TOqvLsVCca478hKemCInZCvIYnE1zvLMAkWcjrO5DW6U1PzocvqoZmN5twD0WaqyLVVl2xoDqIlw1RlKJYouEVizJClo43OXpb3PgckBSk9zcRmGPAk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758777; c=relaxed/simple; bh=u+TeMtdEpcPW9CsO/QEsh8fJjfZoUrTGMJl2odKYICg=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=rHBN+AuxbNjwT6oGTERoDIoUW73XsMzhcR58lmAKgvUgLORU3pEoeOWdPz7xJX5baO6UtZEEOEy0j5zAc9L4XTkVs+lRbMbTzMe7EJxrXgfS5p3AOke5ONosBkEydkc88YFA4mdfjreavPzFSzXn766p/0RsuwCOmFXIjNFTCkI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=swXHZq8M; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="swXHZq8M" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 606EBC433F1; Sat, 30 Mar 2024 00:32:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758776; bh=u+TeMtdEpcPW9CsO/QEsh8fJjfZoUrTGMJl2odKYICg=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=swXHZq8MujBlpcM2e3c4ThiVcTNLgTW4DLgqsnGyJAQrPdMTib0ZIfsFquvac0uR3 5AwA1ZnPA5V99KvwCujO+g98QnWsSxDodgruzgu2jXv254Bm6dlsK9KPISYbVkYtVM VOgTVg/nmS/MatVEo2GL4oIfnEgX/0wdzk6ZFgvS4lW0rUiErag3jx6xLHaUrZ1QOj K72FFkBH4RRvki1HGxpBklikl/CnIL/gGoY1yGo6WDUeWCgy9QHl6U3kK5Oq5G7ddk RjdOHbPx+KjcnB78rXl/qeGZ+wr+4E8MFuCb4mbHtjsQ81P3vbuYfBDS8ChkCvK9tv DQ44KPpR9U2nQ== Date: Fri, 29 Mar 2024 17:32:55 -0700 Subject: [PATCH 01/13] fs: add FS_XFLAG_VERITY for verity files From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867880.1987804.14595583304324961121.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Andrey Albershteyn Add extended attribute FS_XFLAG_VERITY for inodes with fs-verity enabled. Signed-off-by: Andrey Albershteyn [djwong: fix broken verity flag checks] Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- Documentation/filesystems/fsverity.rst | 8 ++++++++ fs/ioctl.c | 11 +++++++++++ include/uapi/linux/fs.h | 1 + 3 files changed, 20 insertions(+) diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index 13e4b18e5dbbb..887cdaf162a99 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -326,6 +326,14 @@ the file has fs-verity enabled. This can perform better than FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require opening the file, and opening verity files can be expensive. +FS_IOC_FSGETXATTR +----------------- + +Since Linux v6.9, the FS_IOC_FSGETXATTR ioctl sets FS_XFLAG_VERITY (0x00020000) +in the returned flags when the file has verity enabled. Note that this attribute +cannot be set with FS_IOC_FSSETXATTR as enabling verity requires input +parameters. See FS_IOC_ENABLE_VERITY. + .. _accessing_verity_files: Accessing verity files diff --git a/fs/ioctl.c b/fs/ioctl.c index 1d5abfdf0f22a..8b5d842b8ee12 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -481,6 +481,8 @@ void fileattr_fill_xflags(struct fileattr *fa, u32 xflags) fa->flags |= FS_DAX_FL; if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT) fa->flags |= FS_PROJINHERIT_FL; + if (fa->fsx_xflags & FS_XFLAG_VERITY) + fa->flags |= FS_VERITY_FL; } EXPORT_SYMBOL(fileattr_fill_xflags); @@ -511,6 +513,8 @@ void fileattr_fill_flags(struct fileattr *fa, u32 flags) fa->fsx_xflags |= FS_XFLAG_DAX; if (fa->flags & FS_PROJINHERIT_FL) fa->fsx_xflags |= FS_XFLAG_PROJINHERIT; + if (fa->flags & FS_VERITY_FL) + fa->fsx_xflags |= FS_XFLAG_VERITY; } EXPORT_SYMBOL(fileattr_fill_flags); @@ -641,6 +645,13 @@ static int fileattr_set_prepare(struct inode *inode, !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) return -EINVAL; + /* + * Verity cannot be changed through FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS. + * See FS_IOC_ENABLE_VERITY. + */ + if ((fa->fsx_xflags ^ old_ma->fsx_xflags) & FS_XFLAG_VERITY) + return -EINVAL; + /* Extent size hints of zero turn off the flags. */ if (fa->fsx_extsize == 0) fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT); diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 45e4e64fd6643..101d1d71242c7 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -158,6 +158,7 @@ struct fsxattr { #define FS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */ #define FS_XFLAG_DAX 0x00008000 /* use DAX for IO */ #define FS_XFLAG_COWEXTSIZE 0x00010000 /* CoW extent size allocator hint */ +#define FS_XFLAG_VERITY 0x00020000 /* fs-verity enabled */ #define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */ /* the read-only stuff doesn't really belong here, but any other place is From patchwork Sat Mar 30 00:33:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611285 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 74FA4811; Sat, 30 Mar 2024 00:33:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758792; cv=none; b=kGpHWw24PfsE+jtQCuG6bfpWU62xv0nBZnynmDY0LaVX827ndhPEXm/OLkzBsb2sOeOAPD8kHJhVsOv4OkeWpJTZMxN/zLBvWG66UBhphoss9EE69si5W75p+yVCH2jWKMJtW5v3YzQwThe2IJAZ3Sbjw+9ll8BDPY7ZPiRxLVM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758792; c=relaxed/simple; bh=RsAyXo8waRT6lhUGr1W6mNDhwALTDa015MFtDJNurwk=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=SOebu07XN+8OxwXCd02xwCBICA0lZHrQotr7lMN5H1WkTbqGrsxY9yFVD0S1M5HmMrv8CpU/Y8FQAucCGOmVYtsYMrXkSrBmSwCFx5QssyHDZfKc+lJ1mwU+0rKSRe5Rf1uiPY5+R2v9d5h/b7vTYN23dqmDy3DdRtpU6LZOhik= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HVGvNor6; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HVGvNor6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1138DC433F1; Sat, 30 Mar 2024 00:33:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758792; bh=RsAyXo8waRT6lhUGr1W6mNDhwALTDa015MFtDJNurwk=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=HVGvNor6lOB1oJsNuI1MRAmURaNyBN5wpZ8TG7KNJSjLo2lIMiM4+Z6b8axmxO5wj j6S5WhduZHGOCG2e3hp7O+HWIJOxifjPu2S4lRHljxnaJ8tLHxsRdG4ridNAlKdg+P /LcKzwjlUPhazFxArvdvUI3y4ZLXkQX85X9++EH+NRgKoyQb0HqYQqwfvWUjcstzM2 B8B7oe1eCE7Q/YBirZsbI+L5mYe4kT2AYwkk8lb+Rws49MzLB9PgK89zLd9J1sd8iR NGkitW2iXjoZiyNoCROg9x39sDw4hxZjdpfhif6FpYnzzjtdULIrYikEeI4/OFYSsI 0qc92FfPCzkjA== Date: Fri, 29 Mar 2024 17:33:11 -0700 Subject: [PATCH 02/13] fsverity: pass tree_blocksize to end_enable_verity() From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867896.1987804.5238508462969286297.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Andrey Albershteyn XFS will need to know tree_blocksize to remove the tree in case of an error. The size is needed to calculate offsets of particular Merkle tree blocks. Signed-off-by: Andrey Albershteyn Reviewed-by: Darrick J. Wong [djwong: I put ebiggers' suggested changes in a separate patch] Signed-off-by: Darrick J. Wong --- fs/btrfs/verity.c | 4 +++- fs/ext4/verity.c | 3 ++- fs/f2fs/verity.c | 3 ++- fs/verity/enable.c | 6 ++++-- include/linux/fsverity.h | 4 +++- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index 4042dd6437aef..647a22e07748e 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -620,6 +620,7 @@ static int btrfs_begin_enable_verity(struct file *filp) * @desc: verity descriptor to write out (NULL in error conditions) * @desc_size: size of the verity descriptor (variable with signatures) * @merkle_tree_size: size of the merkle tree in bytes + * @tree_blocksize: the Merkle tree block size * * If desc is null, then VFS is signaling an error occurred during verity * enable, and we should try to rollback. Otherwise, attempt to finish verity. @@ -627,7 +628,8 @@ static int btrfs_begin_enable_verity(struct file *filp) * Returns 0 on success, negative error code on error. */ static int btrfs_end_enable_verity(struct file *filp, const void *desc, - size_t desc_size, u64 merkle_tree_size) + size_t desc_size, u64 merkle_tree_size, + unsigned int tree_blocksize) { struct btrfs_inode *inode = BTRFS_I(file_inode(filp)); int ret = 0; diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c index 2f37e1ea39551..da2095a813492 100644 --- a/fs/ext4/verity.c +++ b/fs/ext4/verity.c @@ -189,7 +189,8 @@ static int ext4_write_verity_descriptor(struct inode *inode, const void *desc, } static int ext4_end_enable_verity(struct file *filp, const void *desc, - size_t desc_size, u64 merkle_tree_size) + size_t desc_size, u64 merkle_tree_size, + unsigned int tree_blocksize) { struct inode *inode = file_inode(filp); const int credits = 2; /* superblock and inode for ext4_orphan_del() */ diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c index f7bb0c54502c8..8fdac653ff8e8 100644 --- a/fs/f2fs/verity.c +++ b/fs/f2fs/verity.c @@ -144,7 +144,8 @@ static int f2fs_begin_enable_verity(struct file *filp) } static int f2fs_end_enable_verity(struct file *filp, const void *desc, - size_t desc_size, u64 merkle_tree_size) + size_t desc_size, u64 merkle_tree_size, + unsigned int tree_blocksize) { struct inode *inode = file_inode(filp); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); diff --git a/fs/verity/enable.c b/fs/verity/enable.c index c284f46d1b535..04e060880b792 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -274,7 +274,8 @@ static int enable_verity(struct file *filp, * Serialized with ->begin_enable_verity() by the inode lock. */ inode_lock(inode); - err = vops->end_enable_verity(filp, desc, desc_size, params.tree_size); + err = vops->end_enable_verity(filp, desc, desc_size, params.tree_size, + params.block_size); inode_unlock(inode); if (err) { fsverity_err(inode, "%ps() failed with err %d", @@ -300,7 +301,8 @@ static int enable_verity(struct file *filp, rollback: inode_lock(inode); - (void)vops->end_enable_verity(filp, NULL, 0, params.tree_size); + (void)vops->end_enable_verity(filp, NULL, 0, params.tree_size, + params.block_size); inode_unlock(inode); goto out; } diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 1eb7eae580be7..ac58b19f23d32 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -51,6 +51,7 @@ struct fsverity_operations { * @desc: the verity descriptor to write, or NULL on failure * @desc_size: size of verity descriptor, or 0 on failure * @merkle_tree_size: total bytes the Merkle tree took up + * @tree_blocksize: the Merkle tree block size * * If desc == NULL, then enabling verity failed and the filesystem only * must do any necessary cleanups. Else, it must also store the given @@ -65,7 +66,8 @@ struct fsverity_operations { * Return: 0 on success, -errno on failure */ int (*end_enable_verity)(struct file *filp, const void *desc, - size_t desc_size, u64 merkle_tree_size); + size_t desc_size, u64 merkle_tree_size, + unsigned int tree_blocksize); /** * Get the verity descriptor of the given inode. From patchwork Sat Mar 30 00:33:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611286 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 59D477E2; Sat, 30 Mar 2024 00:33:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758808; cv=none; b=SqfUclyCAXN/Zw9bkGXOqpuFL4NLXylogESNABonEoBSMSqCPO58hkciIlwnfto6TBccWaDxJtg9WvNhVoI4TG6YWf1rADDUYIdnJgAV8c3kFfifs9VEX5Ysxx1/jtHQeWkeWe80XdBbpsK/mpk6/FRMq99y4ep29AN+VSDclsQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758808; c=relaxed/simple; bh=XmKecSYBB/1ShfGBv1o8oxx7ZiB3x6lEuMeZH7rQ46c=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Vg0NLojS7uuIkxdJ1DvlVQ9lgMZSMtngdl7UjsjqEvkxgz3IM2ETbg9Mwqaf6QuG/PeTTayIl7SRos/l7UYIdG6iqg2wErUQHHDrIOyOm33G7oYp7eQLfCvJewnswE6lcheZ2gULmL0KZuVA9f9wq7XSPxcD+apoHWxOtClhKa4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QaLQs9b6; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QaLQs9b6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D48F4C433F1; Sat, 30 Mar 2024 00:33:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758807; bh=XmKecSYBB/1ShfGBv1o8oxx7ZiB3x6lEuMeZH7rQ46c=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=QaLQs9b6JEyRL+dAElyPjYxRJM6fUzFQEyaFWOnB85YZT25CMY/AMsL8xv/G7gXgB OUM7MLAsedlqqgP6r91L2WeWWoHEIVAcbYFeS5sGeXW2I5pSfROXiBvh5v/SYpPEeg KzZgpsvart2P0m3OOP4ijXe77FNJus4UKPMsBDoobtJjZXzobLXGbBCPZsuFDrWJ4q rcyXCXa727Z4l/E5ufXqNQnvqF6bUtCxFdlT1ocR0VmIHmEfv3JLEiRh3mb0UjMlg0 bXGnpgZv98LfYDK+u4zjSDNTq1l6JYk8L3DwVRPgLHEZgLaDnu6IapdXUwlytvCH0D xBMRt+704cIkA== Date: Fri, 29 Mar 2024 17:33:27 -0700 Subject: [PATCH 03/13] fsverity: support block-based Merkle tree caching From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867913.1987804.4275409634176359386.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Andrey Albershteyn In the current implementation fs-verity expects filesystem to provide PAGEs filled with Merkle tree blocks. Then, when fs-verity is done with processing the blocks, reference to PAGE is freed. This doesn't fit well with the way XFS manages its memory. To allow XFS integrate fs-verity this patch adds ability to fs-verity verification code to take Merkle tree blocks instead of PAGE reference. This way ext4, f2fs, and btrfs are still able to pass PAGE references and XFS can pass reference to Merkle tree blocks stored in XFS's extended attribute infrastructure. To achieve this, the XFS implementation will implement its own incore merkle tree block cache. These blocks will be passed to fsverity when it needs to read a merkle tree block, and dropped by fsverity when validation completes. The cache will keep track of whether or not a given block has already been verified, which will improve performance on random reads. Signed-off-by: Andrey Albershteyn [djwong: fix uninit err variable, remove dependency on bitmap, apply various suggestions from maintainer, tighten changelog] Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/verity/fsverity_private.h | 37 ++++++++++ fs/verity/open.c | 7 ++ fs/verity/read_metadata.c | 63 ++++++++--------- fs/verity/verify.c | 152 ++++++++++++++++++++++++++++++------------ include/linux/fsverity.h | 76 +++++++++++++++++++++ 5 files changed, 255 insertions(+), 80 deletions(-) diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index b3506f56e180b..c9d97c2bebd84 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -154,4 +154,41 @@ static inline void fsverity_init_signature(void) void __init fsverity_init_workqueue(void); +static inline bool fsverity_caches_blocks(const struct inode *inode) +{ + const struct fsverity_operations *vops = inode->i_sb->s_vop; + + WARN_ON_ONCE(vops->read_merkle_tree_block && + !vops->drop_merkle_tree_block); + + return vops->read_merkle_tree_block != NULL; +} + +static inline bool fsverity_uses_bitmap(const struct fsverity_info *vi, + const struct inode *inode) +{ + if (fsverity_caches_blocks(inode)) + return false; + + /* + * If fs uses block-based Merkle tree caching, then fs-verity must use + * hash_block_verified bitmap as there's no page to mark it with + * PG_checked. + */ + return vi->tree_params.block_size != PAGE_SIZE; +} + +int fsverity_read_merkle_tree_block(struct inode *inode, + const struct merkle_tree_params *params, + u64 pos, unsigned long ra_bytes, + struct fsverity_blockbuf *block); + +/* + * Drop 'block' obtained with ->read_merkle_tree_block(). Calls out back to + * filesystem if ->drop_merkle_tree_block() is set, otherwise, drop the + * reference in the block->context. + */ +void fsverity_drop_merkle_tree_block(struct inode *inode, + struct fsverity_blockbuf *block); + #endif /* _FSVERITY_PRIVATE_H */ diff --git a/fs/verity/open.c b/fs/verity/open.c index fdeb95eca3af3..9603b3a404f74 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -213,7 +213,12 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, if (err) goto fail; - if (vi->tree_params.block_size != PAGE_SIZE) { + /* + * If fs uses block-based Merkle tree cachin, then fs-verity must use + * hash_block_verified bitmap as there's no page to mark it with + * PG_checked. + */ + if (fsverity_uses_bitmap(vi, inode)) { /* * When the Merkle tree block size and page size differ, we use * a bitmap to keep track of which hash blocks have been diff --git a/fs/verity/read_metadata.c b/fs/verity/read_metadata.c index f58432772d9ea..94fffa060f829 100644 --- a/fs/verity/read_metadata.c +++ b/fs/verity/read_metadata.c @@ -14,65 +14,60 @@ static int fsverity_read_merkle_tree(struct inode *inode, const struct fsverity_info *vi, - void __user *buf, u64 offset, int length) + void __user *buf, u64 pos, int length) { - const struct fsverity_operations *vops = inode->i_sb->s_vop; - u64 end_offset; - unsigned int offs_in_page; - pgoff_t index, last_index; + const u64 end_pos = min(pos + length, vi->tree_params.tree_size); + const struct merkle_tree_params *params = &vi->tree_params; + unsigned int offs_in_block = pos & (params->block_size - 1); int retval = 0; int err = 0; - end_offset = min(offset + length, vi->tree_params.tree_size); - if (offset >= end_offset) + if (pos >= end_pos) return 0; - offs_in_page = offset_in_page(offset); - last_index = (end_offset - 1) >> PAGE_SHIFT; /* - * Iterate through each Merkle tree page in the requested range and copy - * the requested portion to userspace. Note that the Merkle tree block - * size isn't important here, as we are returning a byte stream; i.e., - * we can just work with pages even if the tree block size != PAGE_SIZE. + * Iterate through each Merkle tree block in the requested range and + * copy the requested portion to userspace. Note that we are returning + * a byte stream. */ - for (index = offset >> PAGE_SHIFT; index <= last_index; index++) { - unsigned long num_ra_pages = - min_t(unsigned long, last_index - index + 1, - inode->i_sb->s_bdi->io_pages); - unsigned int bytes_to_copy = min_t(u64, end_offset - offset, - PAGE_SIZE - offs_in_page); - struct page *page; - const void *virt; + while (pos < end_pos) { + unsigned long ra_bytes; + unsigned int bytes_to_copy; + struct fsverity_blockbuf block = { + .size = params->block_size, + }; - page = vops->read_merkle_tree_page(inode, index, num_ra_pages); - if (IS_ERR(page)) { - err = PTR_ERR(page); + ra_bytes = min_t(unsigned long, end_pos - pos + 1, + inode->i_sb->s_bdi->io_pages << PAGE_SHIFT); + bytes_to_copy = min_t(u64, end_pos - pos, + params->block_size - offs_in_block); + + err = fsverity_read_merkle_tree_block(inode, &vi->tree_params, + pos - offs_in_block, ra_bytes, &block); + if (err) { fsverity_err(inode, - "Error %d reading Merkle tree page %lu", - err, index); + "Error %d reading Merkle tree block %llu", + err, pos); break; } - virt = kmap_local_page(page); - if (copy_to_user(buf, virt + offs_in_page, bytes_to_copy)) { - kunmap_local(virt); - put_page(page); + if (copy_to_user(buf, block.kaddr + offs_in_block, bytes_to_copy)) { + fsverity_drop_merkle_tree_block(inode, &block); err = -EFAULT; break; } - kunmap_local(virt); - put_page(page); + fsverity_drop_merkle_tree_block(inode, &block); retval += bytes_to_copy; buf += bytes_to_copy; - offset += bytes_to_copy; + pos += bytes_to_copy; if (fatal_signal_pending(current)) { err = -EINTR; break; } cond_resched(); - offs_in_page = 0; + offs_in_block = 0; } return retval ? retval : err; } diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 4fcad0825a120..0b5e11073e883 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -13,14 +13,27 @@ static struct workqueue_struct *fsverity_read_workqueue; /* - * Returns true if the hash block with index @hblock_idx in the tree, located in - * @hpage, has already been verified. + * Returns true if the hash block with index @hblock_idx in the tree has + * already been verified. */ -static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage, +static bool is_hash_block_verified(struct inode *inode, + struct fsverity_blockbuf *block, unsigned long hblock_idx) { unsigned int blocks_per_page; unsigned int i; + struct fsverity_info *vi = inode->i_verity_info; + struct page *hpage; + + /* + * If the filesystem uses block-based caching, then rely on the + * implementation to retain verified status. + */ + if (fsverity_caches_blocks(inode)) + return block->verified; + + /* Otherwise, the filesystem uses page-based caching. */ + hpage = (struct page *)block->context; /* * When the Merkle tree block size and page size are the same, then the @@ -31,7 +44,7 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage, * get evicted and re-instantiated from the backing storage, as new * pages always start out with PG_checked cleared. */ - if (!vi->hash_block_verified) + if (!fsverity_uses_bitmap(vi, inode)) return PageChecked(hpage); /* @@ -90,20 +103,20 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage, */ static bool verify_data_block(struct inode *inode, struct fsverity_info *vi, - const void *data, u64 data_pos, unsigned long max_ra_pages) + const void *data, u64 data_pos, unsigned long max_ra_bytes) { const struct merkle_tree_params *params = &vi->tree_params; const unsigned int hsize = params->digest_size; int level; + int err = 0; + unsigned long ra_bytes; u8 _want_hash[FS_VERITY_MAX_DIGEST_SIZE]; const u8 *want_hash; u8 real_hash[FS_VERITY_MAX_DIGEST_SIZE]; /* The hash blocks that are traversed, indexed by level */ struct { - /* Page containing the hash block */ - struct page *page; - /* Mapped address of the hash block (will be within @page) */ - const void *addr; + /* Buffer containing the hash block */ + struct fsverity_blockbuf block; /* Index of the hash block in the tree overall */ unsigned long index; /* Byte offset of the wanted hash relative to @addr */ @@ -143,11 +156,9 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, for (level = 0; level < params->num_levels; level++) { unsigned long next_hidx; unsigned long hblock_idx; - pgoff_t hpage_idx; - unsigned int hblock_offset_in_page; + u64 hblock_pos; unsigned int hoffset; - struct page *hpage; - const void *haddr; + struct fsverity_blockbuf *block = &hblocks[level].block; /* * The index of the block in the current level; also the index @@ -158,36 +169,34 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, /* Index of the hash block in the tree overall */ hblock_idx = params->level_start[level] + next_hidx; - /* Index of the hash page in the tree overall */ - hpage_idx = hblock_idx >> params->log_blocks_per_page; - - /* Byte offset of the hash block within the page */ - hblock_offset_in_page = - (hblock_idx << params->log_blocksize) & ~PAGE_MASK; + /* Byte offset of the hash block in the tree overall */ + hblock_pos = hblock_idx << params->log_blocksize; /* Byte offset of the hash within the block */ hoffset = (hidx << params->log_digestsize) & (params->block_size - 1); - hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode, - hpage_idx, level == 0 ? min(max_ra_pages, - params->tree_pages - hpage_idx) : 0); - if (IS_ERR(hpage)) { + if (level == 0) + ra_bytes = min(max_ra_bytes, + params->tree_size - hblock_pos); + else + ra_bytes = 0; + + err = fsverity_read_merkle_tree_block(inode, params, hblock_pos, + ra_bytes, block); + if (err) { fsverity_err(inode, - "Error %ld reading Merkle tree page %lu", - PTR_ERR(hpage), hpage_idx); + "Error %d reading Merkle tree block %llu", + err, hblock_pos); goto error; } - haddr = kmap_local_page(hpage) + hblock_offset_in_page; - if (is_hash_block_verified(vi, hpage, hblock_idx)) { - memcpy(_want_hash, haddr + hoffset, hsize); + + if (is_hash_block_verified(inode, block, hblock_idx)) { + memcpy(_want_hash, block->kaddr + hoffset, hsize); want_hash = _want_hash; - kunmap_local(haddr); - put_page(hpage); + fsverity_drop_merkle_tree_block(inode, block); goto descend; } - hblocks[level].page = hpage; - hblocks[level].addr = haddr; hblocks[level].index = hblock_idx; hblocks[level].hoffset = hoffset; hidx = next_hidx; @@ -197,8 +206,8 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, descend: /* Descend the tree verifying hash blocks. */ for (; level > 0; level--) { - struct page *hpage = hblocks[level - 1].page; - const void *haddr = hblocks[level - 1].addr; + struct fsverity_blockbuf *block = &hblocks[level - 1].block; + const void *haddr = block->kaddr; unsigned long hblock_idx = hblocks[level - 1].index; unsigned int hoffset = hblocks[level - 1].hoffset; @@ -211,14 +220,15 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, * idempotent, as the same hash block might be verified by * multiple threads concurrently. */ - if (vi->hash_block_verified) + if (fsverity_caches_blocks(inode)) + block->verified = true; + else if (fsverity_uses_bitmap(vi, inode)) set_bit(hblock_idx, vi->hash_block_verified); else - SetPageChecked(hpage); + SetPageChecked((struct page *)block->context); memcpy(_want_hash, haddr + hoffset, hsize); want_hash = _want_hash; - kunmap_local(haddr); - put_page(hpage); + fsverity_drop_merkle_tree_block(inode, block); } /* Finally, verify the data block. */ @@ -235,16 +245,14 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, params->hash_alg->name, hsize, want_hash, params->hash_alg->name, hsize, real_hash); error: - for (; level > 0; level--) { - kunmap_local(hblocks[level - 1].addr); - put_page(hblocks[level - 1].page); - } + for (; level > 0; level--) + fsverity_drop_merkle_tree_block(inode, &hblocks[level - 1].block); return false; } static bool verify_data_blocks(struct folio *data_folio, size_t len, size_t offset, - unsigned long max_ra_pages) + unsigned long max_ra_bytes) { struct inode *inode = data_folio->mapping->host; struct fsverity_info *vi = inode->i_verity_info; @@ -262,7 +270,7 @@ verify_data_blocks(struct folio *data_folio, size_t len, size_t offset, data = kmap_local_folio(data_folio, offset); valid = verify_data_block(inode, vi, data, pos + offset, - max_ra_pages); + max_ra_bytes); kunmap_local(data); if (!valid) return false; @@ -325,7 +333,7 @@ void fsverity_verify_bio(struct bio *bio) bio_for_each_folio_all(fi, bio) { if (!verify_data_blocks(fi.folio, fi.length, fi.offset, - max_ra_pages)) { + max_ra_pages << PAGE_SHIFT)) { bio->bi_status = BLK_STS_IOERR; break; } @@ -362,3 +370,57 @@ void __init fsverity_init_workqueue(void) if (!fsverity_read_workqueue) panic("failed to allocate fsverity_read_queue"); } + +/** + * fsverity_read_merkle_tree_block() - read Merkle tree block + * @inode: inode to which this Merkle tree blocks belong + * @params: merkle tree parameters + * @pos: byte position within merkle tree + * @ra_bytes: try to read ahead this many btes + * @block: block to be loaded + * + * This function loads data from a merkle tree. + */ +int fsverity_read_merkle_tree_block(struct inode *inode, + const struct merkle_tree_params *params, + u64 pos, unsigned long ra_bytes, + struct fsverity_blockbuf *block) +{ + const struct fsverity_operations *vops = inode->i_sb->s_vop; + unsigned long page_idx; + struct page *page; + unsigned long index; + unsigned int offset_in_page; + + if (fsverity_caches_blocks(inode)) { + block->verified = false; + return vops->read_merkle_tree_block(inode, pos, ra_bytes, + params->log_blocksize, block); + } + + index = pos >> params->log_blocksize; + page_idx = round_down(index, params->blocks_per_page); + offset_in_page = pos & ~PAGE_MASK; + + page = vops->read_merkle_tree_page(inode, page_idx, + ra_bytes >> PAGE_SHIFT); + if (IS_ERR(page)) + return PTR_ERR(page); + + block->kaddr = kmap_local_page(page) + offset_in_page; + block->context = page; + return 0; +} + +void fsverity_drop_merkle_tree_block(struct inode *inode, + struct fsverity_blockbuf *block) +{ + if (fsverity_caches_blocks(inode)) { + inode->i_sb->s_vop->drop_merkle_tree_block(block); + } else { + kunmap_local(block->kaddr); + put_page((struct page *)block->context); + } + block->kaddr = NULL; + block->context = NULL; +} diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index ac58b19f23d32..c3478efd67d62 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -26,6 +26,35 @@ /* Arbitrary limit to bound the kmalloc() size. Can be changed. */ #define FS_VERITY_MAX_DESCRIPTOR_SIZE 16384 +/** + * struct fsverity_blockbuf - Merkle Tree block buffer + * @kaddr: virtual address of the block's data + * @offset: block's offset into Merkle tree + * @size: the Merkle tree block size + * @context: filesystem private context + * @verified: has this buffer been validated? + * + * Buffer containing single Merkle Tree block. These buffers are passed + * - to filesystem, when fs-verity is building merkel tree, + * - from filesystem, when fs-verity is reading merkle tree from a disk. + * Filesystems sets kaddr together with size to point to a memory which contains + * Merkle tree block. Same is done by fs-verity when Merkle tree is need to be + * written down to disk. + * + * While reading the tree, fs-verity calls ->read_merkle_tree_block followed by + * ->drop_merkle_tree_block to let filesystem know that memory can be freed. + * + * The context is optional. This field can be used by filesystem to passthrough + * state from ->read_merkle_tree_block to ->drop_merkle_tree_block. + */ +struct fsverity_blockbuf { + void *kaddr; + u64 offset; + unsigned int verified:1; + unsigned int size; + void *context; +}; + /* Verity operations for filesystems */ struct fsverity_operations { @@ -101,12 +130,43 @@ struct fsverity_operations { * * Note that this must retrieve a *page*, not necessarily a *block*. * + * If this function is implemented, do not implement + * ->read_merkle_tree_block or ->drop_merkle_tree_block. + * * Return: the page on success, ERR_PTR() on failure */ struct page *(*read_merkle_tree_page)(struct inode *inode, pgoff_t index, unsigned long num_ra_pages); + /** + * Read a Merkle tree block of the given inode. + * @inode: the inode + * @pos: byte offset of the block within the Merkle tree + * @ra_bytes: The number of bytes that should be + * prefetched starting at @pos if the page at @pos + * isn't already cached. Implementations may ignore this + * argument; it's only a performance optimization. + * @log_blocksize: log2 of the size of the expected block + * @block: block buffer for filesystem to point it to the block + * + * This can be called at any time on an open verity file. It may be + * called by multiple processes concurrently. + * + * Implementations may cache the @block->verified state in + * ->drop_merkle_tree_block. They must clear the @block->verified + * flag for a cache miss. + * + * If this function is implemented, ->drop_merkle_tree_block must also + * be implemented. + * + * Return: 0 on success, -errno on failure + */ + int (*read_merkle_tree_block)(struct inode *inode, + u64 pos, unsigned long ra_bytes, + unsigned int log_blocksize, + struct fsverity_blockbuf *block); + /** * Write a Merkle tree block to the given inode. * @@ -122,6 +182,22 @@ struct fsverity_operations { */ int (*write_merkle_tree_block)(struct inode *inode, const void *buf, u64 pos, unsigned int size); + + /** + * Release the reference to a Merkle tree block + * + * @block: the block to release + * + * This is called when fs-verity is done with a block obtained with + * ->read_merkle_tree_block(). + * + * Implementations should cache a @block->verified==1 state to avoid + * unnecessary revalidations during later accesses. + * + * If this function is implemented, ->read_merkle_tree_block must also + * be implemented. + */ + void (*drop_merkle_tree_block)(struct fsverity_blockbuf *block); }; #ifdef CONFIG_FS_VERITY From patchwork Sat Mar 30 00:33:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611287 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0B9C1FA5; Sat, 30 Mar 2024 00:33:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758823; cv=none; b=rh86Cpw5vqETI+Ei6lf1/5R62QV46lrA64wJcISr4r5tgyFKx88Sfa0YBg3pH+3WMZeKynVSTMhkK1V+EUlIdg0PtMMhwkVmbxCNSXggTZsevLWbXloATmh5TLdUGNgZxN1RCMWMYeJpHEfzizLivadcP7I9nljbjx3gc0gR2G4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758823; c=relaxed/simple; bh=7y/TBH2WEk8Fj7ho/TkeUus7HMzFwaPsnJQh/oM9+pI=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=mfBRKUB0ZrLjGHC2kzfbQTEkUtlLwASHhE/gC2cNVYNG7OD47FgvbVDaT33AYVe0k/XfT291l98mpC6HdbbI4ATu2GkQUT6yMPqrw8j07HcEVpT2vdXp/FHx3ivqy38q90mLd4aYwAmtOiqITUX3/W3bqoPbts0MTR0Ub46MwwA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=K6dAMLvo; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="K6dAMLvo" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8450FC433F1; Sat, 30 Mar 2024 00:33:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758823; bh=7y/TBH2WEk8Fj7ho/TkeUus7HMzFwaPsnJQh/oM9+pI=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=K6dAMLvoJzkQVcBdNp7o5nCgF/T2p8Sw9REUnlh76Efqob0X1+b9kNkSnEvhHBomK 58A3jRo4VWblrl+cenWZ3sAZUgRrbvxT6dqqur4hFeBqaM/OUbxLQ8L1hfXF02844f GRxYioIsLiizXbnuTBKEBkRidxz499cQGISeYqRMh0SHQbhS3I/wM6x3HT8y1RjlQr IGc+0Al7uaulQR8a24rWngqP4v6tIRYX9PiCataVMEkgTC23A2hrnp+wmCjB85LQME rtbz1I3nkxWXEJ3+XBjyTFoMJGpmQBtv7qi/dwW8AtEEZZWJA6Rd5Xq6Ju9u0nOB5f xvBC+xFVj1lBg== Date: Fri, 29 Mar 2024 17:33:43 -0700 Subject: [PATCH 04/13] fsverity: add per-sb workqueue for post read processing From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867930.1987804.1200988399612926993.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Andrey Albershteyn For XFS, fsverity's global workqueue is not really suitable due to: 1. High priority workqueues are used within XFS to ensure that data IO completion cannot stall processing of journal IO completions. Hence using a WQ_HIGHPRI workqueue directly in the user data IO path is a potential filesystem livelock/deadlock vector. 2. The fsverity workqueue is global - it creates a cross-filesystem contention point. This patch adds per-filesystem, per-cpu workqueue for fsverity work. This allows iomap to add verification work in the read path on BIO completion. Signed-off-by: Andrey Albershteyn [djwong: make it clearer that this workqueue is for verity] Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/super.c | 7 +++++++ fs/verity/verify.c | 20 ++++++++++++++++++++ include/linux/fs.h | 2 ++ include/linux/fsverity.h | 13 +++++++++++++ 4 files changed, 42 insertions(+) diff --git a/fs/super.c b/fs/super.c index 71d9779c42b10..aaa75131f6795 100644 --- a/fs/super.c +++ b/fs/super.c @@ -637,6 +637,13 @@ void generic_shutdown_super(struct super_block *sb) sb->s_dio_done_wq = NULL; } +#ifdef CONFIG_FS_VERITY + if (sb->s_verify_wq) { + destroy_workqueue(sb->s_verify_wq); + sb->s_verify_wq = NULL; + } +#endif + if (sop->put_super) sop->put_super(sb); diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 0b5e11073e883..0417862d5bd4a 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -342,6 +342,26 @@ void fsverity_verify_bio(struct bio *bio) EXPORT_SYMBOL_GPL(fsverity_verify_bio); #endif /* CONFIG_BLOCK */ +int __fsverity_init_verify_wq(struct super_block *sb) +{ + struct workqueue_struct *wq, *old; + + wq = alloc_workqueue("fsverity/%s", WQ_MEM_RECLAIM | WQ_FREEZABLE, 0, + sb->s_id); + if (!wq) + return -ENOMEM; + + /* + * This has to be atomic as readaheads can race to create the + * workqueue. If someone created workqueue before us, we drop ours. + */ + old = cmpxchg(&sb->s_verify_wq, NULL, wq); + if (old) + destroy_workqueue(wq); + return 0; +} +EXPORT_SYMBOL_GPL(__fsverity_init_verify_wq); + /** * fsverity_enqueue_verify_work() - enqueue work on the fs-verity workqueue * @work: the work to enqueue diff --git a/include/linux/fs.h b/include/linux/fs.h index 5d1c33573767f..2fc3c2d218ff8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1230,6 +1230,8 @@ struct super_block { #endif #ifdef CONFIG_FS_VERITY const struct fsverity_operations *s_vop; + /* Completion queue for post read verification */ + struct workqueue_struct *s_verify_wq; #endif #if IS_ENABLED(CONFIG_UNICODE) struct unicode_map *s_encoding; diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index c3478efd67d62..495708fb1f26a 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -252,6 +252,14 @@ bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset); void fsverity_verify_bio(struct bio *bio); void fsverity_enqueue_verify_work(struct work_struct *work); +int __fsverity_init_verify_wq(struct super_block *sb); +static inline int fsverity_init_verify_wq(struct super_block *sb) +{ + if (sb->s_verify_wq) + return 0; + return __fsverity_init_verify_wq(sb); +} + #else /* !CONFIG_FS_VERITY */ static inline struct fsverity_info *fsverity_get_info(const struct inode *inode) @@ -329,6 +337,11 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work) WARN_ON_ONCE(1); } +static inline int fsverity_init_verify_wq(struct super_block *sb) +{ + return -EOPNOTSUPP; +} + #endif /* !CONFIG_FS_VERITY */ static inline bool fsverity_verify_folio(struct folio *folio) From patchwork Sat Mar 30 00:33:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611288 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7C707E2; Sat, 30 Mar 2024 00:33:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758839; cv=none; b=d9jx7eYG9XTqbnW/U95lK2Fk55E2uOP/NfJ/RNgBC0NGP+VmE+yki4KvYuuSLbZvfHWhwJw2EWJiP6BMXXOj21SrzPkcCA7Gp766ICm9qkZmOg1HzKe27KAQvh161/0CPM0wxQ6fxcYX/Gk6P3mrBQ+vaF6lpydtFhKcuiqh2h0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758839; c=relaxed/simple; bh=uyfcszPMXi6qb94I/M4zs1L7DSTgdq1xEh54Pf2p6bE=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=p2IsZHiz7Ph4FCfwbzFp43vTvVeM9I+1UQRnHS5uMmIh0Q6XVntkjGI8GnDm4XliRxOAWqt2IaeaNtN46FpX/aoWTzDkVjDNT/2RNdLxfTwmDUe6jYuCE/sruGIfygY5cRR86lxNqmwFkgG5EZH2Gx4bVKhnPWpAdswLr/l6uGM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Y6QnUtyh; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Y6QnUtyh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3A358C433C7; Sat, 30 Mar 2024 00:33:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758839; bh=uyfcszPMXi6qb94I/M4zs1L7DSTgdq1xEh54Pf2p6bE=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=Y6QnUtyha8tRkR9ZoirPgoq9s3d4fxObDtcXCUkfiLR+rLZjlL/PRTVwL5j7CyNuF Ui4TxBUK2pLVz0wZ7yXkLRrIrE+p8NP4C5FtfsGXGkrxt+ELnCVgcJDmcvOyqEfVY1 36l1XxxPlgPZKnR0/vijJW6hVRq6ujj3UYeckqsh6eWsMZBdkluF6orP2BAfWeaeMn gBhZbJNA4oZL3KUh2yp/aDIs7KN+wZ4Rtxb0yFWpq8opi3xAIMhV4G5KnCWub29EDm WFriax8vfYkVkoe0EFN5nHH4yey9yzaLBJ9e8JJ4aYqysOvOwdBDAyxYj9IpQq5F7P lpalizAk7IXIQ== Date: Fri, 29 Mar 2024 17:33:58 -0700 Subject: [PATCH 05/13] fsverity: add tracepoints From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867947.1987804.12550294266527349369.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Andrey Albershteyn fs-verity previously had debug printk but it was removed. This patch adds trace points to the same places where printk were used (with a few additional ones). Signed-off-by: Andrey Albershteyn Reviewed-by: Darrick J. Wong [djwong: fix formatting] Signed-off-by: Darrick J. Wong --- MAINTAINERS | 1 fs/verity/enable.c | 4 + fs/verity/fsverity_private.h | 2 + fs/verity/init.c | 1 fs/verity/verify.c | 8 ++ include/trace/events/fsverity.h | 142 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 158 insertions(+) create mode 100644 include/trace/events/fsverity.h diff --git a/MAINTAINERS b/MAINTAINERS index aa3b947fb0801..a301e9fe0c021 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8835,6 +8835,7 @@ T: git https://git.kernel.org/pub/scm/fs/fsverity/linux.git F: Documentation/filesystems/fsverity.rst F: fs/verity/ F: include/linux/fsverity.h +F: include/trace/events/fsverity.h F: include/uapi/linux/fsverity.h FT260 FTDI USB-HID TO I2C BRIDGE DRIVER diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 04e060880b792..9f743f9160100 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -227,6 +227,8 @@ static int enable_verity(struct file *filp, if (err) goto out; + trace_fsverity_enable(inode, ¶ms); + /* * Start enabling verity on this file, serialized by the inode lock. * Fail if verity is already enabled or is already being enabled. @@ -269,6 +271,8 @@ static int enable_verity(struct file *filp, goto rollback; } + trace_fsverity_tree_done(inode, vi, ¶ms); + /* * Tell the filesystem to finish enabling verity on the file. * Serialized with ->begin_enable_verity() by the inode lock. diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index c9d97c2bebd84..d4a9178f9e827 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -191,4 +191,6 @@ int fsverity_read_merkle_tree_block(struct inode *inode, void fsverity_drop_merkle_tree_block(struct inode *inode, struct fsverity_blockbuf *block); +#include + #endif /* _FSVERITY_PRIVATE_H */ diff --git a/fs/verity/init.c b/fs/verity/init.c index cb2c9aac61ed0..3769d2dc9e3b4 100644 --- a/fs/verity/init.c +++ b/fs/verity/init.c @@ -5,6 +5,7 @@ * Copyright 2019 Google LLC */ +#define CREATE_TRACE_POINTS #include "fsverity_private.h" #include diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 0417862d5bd4a..85d8d2fcce9ab 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -122,6 +122,9 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, /* Byte offset of the wanted hash relative to @addr */ unsigned int hoffset; } hblocks[FS_VERITY_MAX_LEVELS]; + + trace_fsverity_verify_data_block(inode, params, data_pos); + /* * The index of the previous level's block within that level; also the * index of that block's hash within the current level. @@ -194,6 +197,9 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, if (is_hash_block_verified(inode, block, hblock_idx)) { memcpy(_want_hash, block->kaddr + hoffset, hsize); want_hash = _want_hash; + trace_fsverity_merkle_hit(inode, data_pos, hblock_pos, + level, + hoffset >> params->log_digestsize); fsverity_drop_merkle_tree_block(inode, block); goto descend; } @@ -228,6 +234,8 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, SetPageChecked((struct page *)block->context); memcpy(_want_hash, haddr + hoffset, hsize); want_hash = _want_hash; + trace_fsverity_verify_merkle_block(inode, block->offset, + level, hoffset >> params->log_digestsize); fsverity_drop_merkle_tree_block(inode, block); } diff --git a/include/trace/events/fsverity.h b/include/trace/events/fsverity.h new file mode 100644 index 0000000000000..f08d3eb3368f3 --- /dev/null +++ b/include/trace/events/fsverity.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fsverity + +#if !defined(_TRACE_FSVERITY_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_FSVERITY_H + +#include + +struct fsverity_descriptor; +struct merkle_tree_params; +struct fsverity_info; + +TRACE_EVENT(fsverity_enable, + TP_PROTO(const struct inode *inode, + const struct merkle_tree_params *params), + TP_ARGS(inode, params), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, data_size) + __field(unsigned int, block_size) + __field(unsigned int, num_levels) + __field(u64, tree_size) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->data_size = i_size_read(inode); + __entry->block_size = params->block_size; + __entry->num_levels = params->num_levels; + __entry->tree_size = params->tree_size; + ), + TP_printk("ino %lu data size %llu tree size %llu block size %u levels %u", + (unsigned long) __entry->ino, + __entry->data_size, + __entry->tree_size, + __entry->block_size, + __entry->num_levels) +); + +TRACE_EVENT(fsverity_tree_done, + TP_PROTO(const struct inode *inode, const struct fsverity_info *vi, + const struct merkle_tree_params *params), + TP_ARGS(inode, vi, params), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(unsigned int, levels) + __field(unsigned int, block_size) + __field(u64, tree_size) + __dynamic_array(u8, root_hash, params->digest_size) + __dynamic_array(u8, file_digest, params->digest_size) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->levels = params->num_levels; + __entry->block_size = params->block_size; + __entry->tree_size = params->tree_size; + memcpy(__get_dynamic_array(root_hash), vi->root_hash, __get_dynamic_array_len(root_hash)); + memcpy(__get_dynamic_array(file_digest), vi->file_digest, __get_dynamic_array_len(file_digest)); + ), + TP_printk("ino %lu levels %d block_size %d tree_size %lld root_hash %s digest %s", + (unsigned long) __entry->ino, + __entry->levels, + __entry->block_size, + __entry->tree_size, + __print_hex_str(__get_dynamic_array(root_hash), __get_dynamic_array_len(root_hash)), + __print_hex_str(__get_dynamic_array(file_digest), __get_dynamic_array_len(file_digest))) +); + +TRACE_EVENT(fsverity_verify_data_block, + TP_PROTO(const struct inode *inode, + const struct merkle_tree_params *params, + u64 data_pos), + TP_ARGS(inode, params, data_pos), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, data_pos) + __field(unsigned int, block_size) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->data_pos = data_pos; + __entry->block_size = params->block_size; + ), + TP_printk("ino %lu pos %lld merkle_blocksize %u", + (unsigned long) __entry->ino, + __entry->data_pos, + __entry->block_size) +); + +TRACE_EVENT(fsverity_merkle_hit, + TP_PROTO(const struct inode *inode, u64 data_pos, u64 merkle_pos, + unsigned int level, unsigned int hidx), + TP_ARGS(inode, data_pos, merkle_pos, level, hidx), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, data_pos) + __field(u64, merkle_pos) + __field(unsigned int, level) + __field(unsigned int, hidx) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->data_pos = data_pos; + __entry->merkle_pos = merkle_pos; + __entry->level = level; + __entry->hidx = hidx; + ), + TP_printk("ino %lu data_pos %llu merkle_pos %llu level %u hidx %u", + (unsigned long) __entry->ino, + __entry->data_pos, + __entry->merkle_pos, + __entry->level, + __entry->hidx) +); + +TRACE_EVENT(fsverity_verify_merkle_block, + TP_PROTO(const struct inode *inode, u64 merkle_pos, unsigned int level, + unsigned int hidx), + TP_ARGS(inode, merkle_pos, level, hidx), + TP_STRUCT__entry( + __field(ino_t, ino) + __field(u64, merkle_pos) + __field(unsigned int, level) + __field(unsigned int, hidx) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->merkle_pos = merkle_pos; + __entry->level = level; + __entry->hidx = hidx; + ), + TP_printk("ino %lu merkle_pos %llu level %u hidx %u", + (unsigned long) __entry->ino, + __entry->merkle_pos, + __entry->level, + __entry->hidx) +); + +#endif /* _TRACE_FSVERITY_H */ + +/* This part must be outside protection */ +#include From patchwork Sat Mar 30 00:34:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611289 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 22DA415A5; Sat, 30 Mar 2024 00:34:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758855; cv=none; b=K8eNHj2ElREO3R7CgXlQbfU9OA9Ix/VRYBcIWg/Al3TxuamhY2sIRww0iNtqr05et0Eb81kXkd7Rko90aai55DYdUOaIqn27tKevLw6CyEwQU9aMudTF8ygo9Q2i4U3Cod+c2qWJzpg78eGxrjLfgLo7EqZNeZMdqipD52xAza0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758855; c=relaxed/simple; bh=w+TL+zO+c6nqQijTthaLxqD4uljZAOe7pLb/4Ci2LxU=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=om65ArDoVL6bpkytkJWfA/gwK+L4ba0t0TCT9p1gfnF9p6pKaZ1O1MJoWILgS4ypDblVz6cWGTImFpaTcTjQc2o21rSyK31rMCtUWYYvyrflDIH6kI2bIAxCtD5ClSfDj60R9Y3TxL3TAd9nyh01z265A4Tg5ns3BzImP6UZSio= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=aaoVwK0Q; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="aaoVwK0Q" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E7A98C433F1; Sat, 30 Mar 2024 00:34:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758855; bh=w+TL+zO+c6nqQijTthaLxqD4uljZAOe7pLb/4Ci2LxU=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=aaoVwK0QUV+g57yQbbG6adfxoTh36UgmJ1IfDTN91/Cg9WRUOxlSklahPwv9slXFa ko0L4aysC8f4rZMvvVvAQjGzDMqgoW8b5PPIPaTmosznHMYPlmy12ObuMm/dycwKat cAJ97LjiRNnLzDZMeohZ4OdeTHe7JQq9kqbABbpnGe1dPXQpLo8xePDg9+0tTarRe8 XHNe4J9oNKd5hNG1Txqmwi3HnHwDgZ7xFiN5GwtUL4A2s2/KBY35ulFukq/PEfNXgG GncKFRx7fRTQ65DD3wX+JWr0VXwrVL6tZnjQQfpokLlbtqYv8RckbdHR+uRGPIDL6D g8tIrqucWAA5g== Date: Fri, 29 Mar 2024 17:34:14 -0700 Subject: [PATCH 06/13] fsverity: send the level of the merkle tree block to ->read_merkle_tree_block From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867965.1987804.16949621858616176182.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong When fsverity needs to pull in a merkle tree block for file data verification, it knows the level of the block within the tree. For XFS, we will cache the blocks in memory ourselves, and it is advantageous to make higher level nodes more resistant to memory reclamation. Therefore, we need to pass the anticipated level to the ->read_merkle_tree_block functions to enable this kind of caching. Establish level == -1 to mean streaming read (e.g. downloading the merkle tree). Signed-off-by: Darrick J. Wong --- fs/verity/fsverity_private.h | 2 +- fs/verity/read_metadata.c | 2 +- fs/verity/verify.c | 22 +++++++++++++++++----- include/linux/fsverity.h | 32 ++++++++++++++++++++++---------- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index d4a9178f9e827..de8798f141d4a 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -180,7 +180,7 @@ static inline bool fsverity_uses_bitmap(const struct fsverity_info *vi, int fsverity_read_merkle_tree_block(struct inode *inode, const struct merkle_tree_params *params, - u64 pos, unsigned long ra_bytes, + int level, u64 pos, unsigned long ra_bytes, struct fsverity_blockbuf *block); /* diff --git a/fs/verity/read_metadata.c b/fs/verity/read_metadata.c index 94fffa060f829..87cc6f2896633 100644 --- a/fs/verity/read_metadata.c +++ b/fs/verity/read_metadata.c @@ -43,7 +43,7 @@ static int fsverity_read_merkle_tree(struct inode *inode, params->block_size - offs_in_block); err = fsverity_read_merkle_tree_block(inode, &vi->tree_params, - pos - offs_in_block, ra_bytes, &block); + -1, pos - offs_in_block, ra_bytes, &block); if (err) { fsverity_err(inode, "Error %d reading Merkle tree block %llu", diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 85d8d2fcce9ab..c4ebf85ba2c79 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -185,8 +185,8 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, else ra_bytes = 0; - err = fsverity_read_merkle_tree_block(inode, params, hblock_pos, - ra_bytes, block); + err = fsverity_read_merkle_tree_block(inode, params, level, + hblock_pos, ra_bytes, block); if (err) { fsverity_err(inode, "Error %d reading Merkle tree block %llu", @@ -403,6 +403,8 @@ void __init fsverity_init_workqueue(void) * fsverity_read_merkle_tree_block() - read Merkle tree block * @inode: inode to which this Merkle tree blocks belong * @params: merkle tree parameters + * @level: expected level of the block; level 0 are the leaves, -1 means a + * streaming read * @pos: byte position within merkle tree * @ra_bytes: try to read ahead this many btes * @block: block to be loaded @@ -411,7 +413,7 @@ void __init fsverity_init_workqueue(void) */ int fsverity_read_merkle_tree_block(struct inode *inode, const struct merkle_tree_params *params, - u64 pos, unsigned long ra_bytes, + int level, u64 pos, unsigned long ra_bytes, struct fsverity_blockbuf *block) { const struct fsverity_operations *vops = inode->i_sb->s_vop; @@ -420,10 +422,20 @@ int fsverity_read_merkle_tree_block(struct inode *inode, unsigned long index; unsigned int offset_in_page; + block->offset = pos; + block->size = params->block_size; + if (fsverity_caches_blocks(inode)) { + struct fsverity_readmerkle req = { + .inode = inode, + .level = level, + .num_levels = params->num_levels, + .log_blocksize = params->log_blocksize, + .ra_bytes = ra_bytes, + }; + block->verified = false; - return vops->read_merkle_tree_block(inode, pos, ra_bytes, - params->log_blocksize, block); + return vops->read_merkle_tree_block(&req, block); } index = pos >> params->log_blocksize; diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 495708fb1f26a..52de58d6f021f 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -55,6 +55,26 @@ struct fsverity_blockbuf { void *context; }; +/** + * struct fsverity_readmerkle - Request to read a Merkle Tree block buffer + * @inode: the inode to read + * @level: expected level of the block; level 0 are the leaves, -1 means a + * streaming read + * @num_levels: number of levels in the tree total + * @log_blocksize: log2 of the size of the expected block + * @ra_bytes: The number of bytes that should be prefetched starting at pos + * if the page at @block->offset isn't already cached. + * Implementations may ignore this argument; it's only a + * performance optimization. + */ +struct fsverity_readmerkle { + struct inode *inode; + unsigned long ra_bytes; + int level; + int num_levels; + u8 log_blocksize; +}; + /* Verity operations for filesystems */ struct fsverity_operations { @@ -141,13 +161,7 @@ struct fsverity_operations { /** * Read a Merkle tree block of the given inode. - * @inode: the inode - * @pos: byte offset of the block within the Merkle tree - * @ra_bytes: The number of bytes that should be - * prefetched starting at @pos if the page at @pos - * isn't already cached. Implementations may ignore this - * argument; it's only a performance optimization. - * @log_blocksize: log2 of the size of the expected block + * @req: read request; see struct fsverity_readmerkle * @block: block buffer for filesystem to point it to the block * * This can be called at any time on an open verity file. It may be @@ -162,9 +176,7 @@ struct fsverity_operations { * * Return: 0 on success, -errno on failure */ - int (*read_merkle_tree_block)(struct inode *inode, - u64 pos, unsigned long ra_bytes, - unsigned int log_blocksize, + int (*read_merkle_tree_block)(const struct fsverity_readmerkle *req, struct fsverity_blockbuf *block); /** From patchwork Sat Mar 30 00:34:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611290 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F39571C3D; Sat, 30 Mar 2024 00:34:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758871; cv=none; b=ptaesI9tmeSicSYSEqfr5WvF+sQ7oPXNlRdYzmWDFGs04hM0V5u0nxHsJ5RiTH8+fFn7AqcRUgFkMZxv11tl6UYoRsw+D+1LLQ8Unc1i4/DyX5T3d0cnO3m6aK+xmzH86KXRunoZMuLn2XZeP5DK2PdGWG5LhAXvQGGHiDeWWko= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758871; c=relaxed/simple; bh=svrSqktoBzWVoqHP062pgAOGwYwEZp5JltSD1P7BLQQ=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AOKMP3awuWs2hvvpNBGrisOV8LcQ2rKRkv0EWAbs7XDxx9Ey4Sq97P9L71YL8RrXakjBdS583uGQynUfc10hy09SICYUZBdHFUKXZmcdqsz33tKKb6PLUEHkfIiI60wetMhSxAxADPtDF4KC+XikKF2qWyK1u37ZCxFOwQutW0w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=n4rECkrH; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="n4rECkrH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 90019C433F1; Sat, 30 Mar 2024 00:34:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758870; bh=svrSqktoBzWVoqHP062pgAOGwYwEZp5JltSD1P7BLQQ=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=n4rECkrHQF+HhXhdtWkJe2Z3eWi72LkxMQAA3PEdkBFtqYGvdpXoUotwBI4Ni5w58 DK2WsYaXEOaMUYXANXiFa3mgNdnTsH08HSvAkAqfXsJ5Jo/kKYtbqXjNhupJF/PD2c brBg2Um0LnD7ShFXWmfvlZYuRTSeOW8cQOVLgJm2WI3ZsH4XHLCMAwk0LmzQ67b1OW 0n7nUw30gMlrinryYscvShDcfqJSGIdHrmWSf9AB3KL1EidoK0OsUCYCUqENppgW0w 7P6LwYpM201Y5lkxlNiWzwZK6WSgs3gYrLS/yxIHMiry5nwNrP4HM/bX76D1IEZtTI rmj4KD5Y0wiUg== Date: Fri, 29 Mar 2024 17:34:30 -0700 Subject: [PATCH 07/13] fsverity: pass the new tree size and block size to ->begin_enable_verity From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867981.1987804.2143506550606185399.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong When starting up the process of enabling fsverity on a file, pass the new size of the merkle tree and the merkle tree block size to the fs implementation. XFS will want this information later to try to clean out a failed previous enablement attempt. Signed-off-by: Darrick J. Wong --- fs/btrfs/verity.c | 3 ++- fs/ext4/verity.c | 3 ++- fs/f2fs/verity.c | 3 ++- fs/verity/enable.c | 3 ++- include/linux/fsverity.h | 5 ++++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index 647a22e07748e..a3235571bf02d 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -578,7 +578,8 @@ static int finish_verity(struct btrfs_inode *inode, const void *desc, * * Returns 0 on success, negative error code on failure. */ -static int btrfs_begin_enable_verity(struct file *filp) +static int btrfs_begin_enable_verity(struct file *filp, u64 merkle_tree_size, + unsigned int tree_blocksize) { struct btrfs_inode *inode = BTRFS_I(file_inode(filp)); struct btrfs_root *root = inode->root; diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c index da2095a813492..a8ae8c912cb5d 100644 --- a/fs/ext4/verity.c +++ b/fs/ext4/verity.c @@ -99,7 +99,8 @@ static int pagecache_write(struct inode *inode, const void *buf, size_t count, return 0; } -static int ext4_begin_enable_verity(struct file *filp) +static int ext4_begin_enable_verity(struct file *filp, u64 merkle_tree_size, + unsigned int tree_blocksize) { struct inode *inode = file_inode(filp); const int credits = 2; /* superblock and inode for ext4_orphan_add() */ diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c index 8fdac653ff8e8..595d702c2c5c4 100644 --- a/fs/f2fs/verity.c +++ b/fs/f2fs/verity.c @@ -115,7 +115,8 @@ struct fsverity_descriptor_location { __le64 pos; }; -static int f2fs_begin_enable_verity(struct file *filp) +static int f2fs_begin_enable_verity(struct file *filp, u64 merkle_tree_size, + unsigned int tree_blocksize) { struct inode *inode = file_inode(filp); int err; diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 9f743f9160100..1d4a6de960149 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -237,7 +237,8 @@ static int enable_verity(struct file *filp, if (IS_VERITY(inode)) err = -EEXIST; else - err = vops->begin_enable_verity(filp); + err = vops->begin_enable_verity(filp, params.tree_size, + params.block_size); inode_unlock(inode); if (err) goto out; diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 52de58d6f021f..030d7094d80fc 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -82,6 +82,8 @@ struct fsverity_operations { * Begin enabling verity on the given file. * * @filp: a readonly file descriptor for the file + * @merkle_tree_size: total bytes the new Merkle tree will take up + * @tree_blocksize: the new Merkle tree block size * * The filesystem must do any needed filesystem-specific preparations * for enabling verity, e.g. evicting inline data. It also must return @@ -91,7 +93,8 @@ struct fsverity_operations { * * Return: 0 on success, -errno on failure */ - int (*begin_enable_verity)(struct file *filp); + int (*begin_enable_verity)(struct file *filp, u64 merkle_tree_size, + unsigned int tree_blocksize); /** * End enabling verity on the given file. From patchwork Sat Mar 30 00:34:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611291 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B6A3A7E2; Sat, 30 Mar 2024 00:34:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758886; cv=none; b=pvLeIFrZAiNBs7GsmRHjqGU/x5LakqdFGW+vLhKSkAhyKAOJ3bM5Ron5OhXRMQq+KwycDiMQz8k2aoPIgESch1Fc8n7jOQB2Ko2MuRTdwt0RYyoJxj/dwwll6HGjyaZxGmHkAIAkW99R0Cd3WVozdH1fv/wLnd9CrBlQCmqdVNs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758886; c=relaxed/simple; bh=xxo5EvCmfH/v7ww4RgxdwOktYkge9I8zSgMzTqpjYP8=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JcB5bGQoDAOiIEAfJgztJM3LM9EVNtEGTOnqE44kBVqHw0+TCvr5JKATyT0sj1vTUISiCQbfVrMeVJHYrpJf5WlLqeNPjjuhxGyxi4gR0TJB+GrhboCL+EqD+qKcwcSQvgzKKbag/6PueydiYhtVUzKNHfS6Wtd2uAdV0Kne5sQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tZoOkeYx; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="tZoOkeYx" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 446ADC43390; Sat, 30 Mar 2024 00:34:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758886; bh=xxo5EvCmfH/v7ww4RgxdwOktYkge9I8zSgMzTqpjYP8=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=tZoOkeYxPLPwBnHe8xT0/+6r2niIFmFMzGUuKDjG0Q8z/bXpajG/0ppAL3Mju6EXv OAWfxsw29fP0rhImvQWP7VmNu7iIH7VB44zPm403cU31Kt2q/b+HtNwN8kjIaKQIkC ozRk65cndJ4HXtry2+iFJa/+zVu43D5HgFEOd9o2cZncYQfqUrKAMwaFSjS+st4NS9 X04h2gdpnFH+pS6eotxKFpCTj2gxRLqBbidAOvoJLbyh9ah/qUZAAjKxMn07BAkiPb w07IM6fhIAvY4xHnBZGQJDhtDU4az6JhxXKgCfiYoIsRlwqjnapqsi/qYdrHUNjSqy Bd6mTW0aKRC3A== Date: Fri, 29 Mar 2024 17:34:45 -0700 Subject: [PATCH 08/13] fsverity: expose merkle tree geometry to callers From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175867998.1987804.8334701724660862039.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Create a function that will return selected information about the geometry of the merkle tree. Online fsck for XFS will need this piece to perform basic checks of the merkle tree. Signed-off-by: Darrick J. Wong --- fs/verity/open.c | 26 ++++++++++++++++++++++++++ include/linux/fsverity.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/fs/verity/open.c b/fs/verity/open.c index 9603b3a404f74..7a86407732c41 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -412,6 +412,32 @@ void __fsverity_cleanup_inode(struct inode *inode) } EXPORT_SYMBOL_GPL(__fsverity_cleanup_inode); +/** + * fsverity_merkle_tree_geometry() - return Merkle tree geometry + * @inode: the inode for which the Merkle tree is being built + * @block_size: size of a merkle tree block, in bytes + * @tree_size: size of the merkle tree, in bytes + */ +int fsverity_merkle_tree_geometry(struct inode *inode, unsigned int *block_size, + u64 *tree_size) +{ + struct fsverity_info *vi; + int error; + + if (!IS_VERITY(inode)) + return -EOPNOTSUPP; + + error = ensure_verity_info(inode); + if (error) + return error; + + vi = fsverity_get_info(inode); + *block_size = vi->tree_params.block_size; + *tree_size = vi->tree_params.tree_size; + return 0; +} +EXPORT_SYMBOL_GPL(fsverity_merkle_tree_geometry); + void __init fsverity_init_info_cache(void) { fsverity_info_cachep = KMEM_CACHE_USERCOPY( diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 030d7094d80fc..5b1485a842983 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -245,6 +245,9 @@ int __fsverity_file_open(struct inode *inode, struct file *filp); int __fsverity_prepare_setattr(struct dentry *dentry, struct iattr *attr); void __fsverity_cleanup_inode(struct inode *inode); +int fsverity_merkle_tree_geometry(struct inode *inode, unsigned int *block_size, + u64 *tree_size); + /** * fsverity_cleanup_inode() - free the inode's verity info, if present * @inode: an inode being evicted From patchwork Sat Mar 30 00:35:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611292 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 04A7015A5; Sat, 30 Mar 2024 00:35:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758902; cv=none; b=NqXOtf9NhXIx80W8RhejZfqUyHplWYkIeYs1AKb5ZabtpQclnHYmnyaTeD6zdJK/Z6rXQ+93g1ZHNNcOC3lXycu3Y6CKeo+7qdjzv8467mWtBpc/vDoSzzwXvh4H9t/ooo1BaCBUn8n+MCyH/GUkPhivrUWPzB9Nf8Dq2Wx1hUA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758902; c=relaxed/simple; bh=Novm5Xsymnm8HBrtcUcW0Tf22zMFU4thnehZpHYG+zs=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=p/KW8M2Rdpwk27Y3RC/huxwHPfG3BggDZ117hbCot9xhcVmJ3AFj6Wlnptuloo8i8sWjbLR+9snzGA2A6MouupRDZNnwmh0hFIgFocW3v1a9W+sUlmcWBCsx0tOvHZgNiY7lOjPqJTmEPkTfHODExWmvf61PAfZ872dbXzMHHzo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PBrcc53V; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PBrcc53V" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CDBDCC433F1; Sat, 30 Mar 2024 00:35:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758901; bh=Novm5Xsymnm8HBrtcUcW0Tf22zMFU4thnehZpHYG+zs=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=PBrcc53VsEL7URzqOCB2B6zBmLTiJAgNYkZbRbWvIuW8xRh4PyAXBCcrNe/kEvTUY Y7F4XfTVQ+RSc4p5t0eLK51GIccMlM0DOz1itJa6t0jGeiwuarA6chQi7OSPjJTAih /vxQwfXAWzTZ9FFM9O6yThZSHsSMsB7mfLSemiW0KPTKLPa80teYwGlGaKXlCMdqxY 62rjFvGzBwi2jKWjBX+ma8vJ0DaiHJ+nIogvaMpuNbrJavTFXL3+xepZ9telLUXEaa i8oyxdvmwTfbgqndE02ekBzqmiaq6qUQNPbMStq2pW+0HOqRQc/96r4ICUq94up+Qo 1nj5Px/9av3tg== Date: Fri, 29 Mar 2024 17:35:01 -0700 Subject: [PATCH 09/13] fsverity: box up the write_merkle_tree_block parameters too From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175868014.1987804.14065724867005749327.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Box up the tree write request parameters into a structure so that we can add more in the next few patches. Signed-off-by: Darrick J. Wong --- fs/btrfs/verity.c | 6 ++++-- fs/ext4/verity.c | 7 +++++-- fs/f2fs/verity.c | 7 +++++-- fs/verity/enable.c | 5 ++++- include/linux/fsverity.h | 21 ++++++++++++++++++--- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index a3235571bf02d..576547c0f9e54 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -790,9 +790,11 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, * * Returns 0 on success or negative error code on failure */ -static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf, - u64 pos, unsigned int size) +static int btrfs_write_merkle_tree_block(const struct fsverity_writemerkle *req, + const void *buf, u64 pos, + unsigned int size) { + struct inode *inode = req->inode; loff_t merkle_pos = merkle_file_pos(inode); if (merkle_pos < 0) diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c index a8ae8c912cb5d..27eb2d51cce2f 100644 --- a/fs/ext4/verity.c +++ b/fs/ext4/verity.c @@ -382,9 +382,12 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode, return folio_file_page(folio, index); } -static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf, - u64 pos, unsigned int size) +static int ext4_write_merkle_tree_block(const struct fsverity_writemerkle *req, + const void *buf, u64 pos, + unsigned int size) { + struct inode *inode = req->inode; + pos += ext4_verity_metadata_pos(inode); return pagecache_write(inode, buf, size, pos); diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c index 595d702c2c5c4..f8d974818f3bb 100644 --- a/fs/f2fs/verity.c +++ b/fs/f2fs/verity.c @@ -279,9 +279,12 @@ static struct page *f2fs_read_merkle_tree_page(struct inode *inode, return folio_file_page(folio, index); } -static int f2fs_write_merkle_tree_block(struct inode *inode, const void *buf, - u64 pos, unsigned int size) +static int f2fs_write_merkle_tree_block(const struct fsverity_writemerkle *req, + const void *buf, u64 pos, + unsigned int size) { + struct inode *inode = req->inode; + pos += f2fs_verity_metadata_pos(inode); return pagecache_write(inode, buf, size, pos); diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 1d4a6de960149..233b20fb12ff5 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -50,10 +50,13 @@ static int write_merkle_tree_block(struct inode *inode, const u8 *buf, unsigned long index, const struct merkle_tree_params *params) { + struct fsverity_writemerkle req = { + .inode = inode, + }; u64 pos = (u64)index << params->log_blocksize; int err; - err = inode->i_sb->s_vop->write_merkle_tree_block(inode, buf, pos, + err = inode->i_sb->s_vop->write_merkle_tree_block(&req, buf, pos, params->block_size); if (err) fsverity_err(inode, "Error %d writing Merkle tree block %lu", diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 5b1485a842983..5dacd30d65353 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -75,6 +75,20 @@ struct fsverity_readmerkle { u8 log_blocksize; }; +/** + * struct fsverity_writemerkle - Request to write a Merkle Tree block buffer + * @inode: the inode to read + * @level: level of the block; level 0 are the leaves + * @num_levels: number of levels in the tree total + * @log_blocksize: log2 of the size of the block + */ +struct fsverity_writemerkle { + struct inode *inode; + int level; + int num_levels; + u8 log_blocksize; +}; + /* Verity operations for filesystems */ struct fsverity_operations { @@ -185,7 +199,7 @@ struct fsverity_operations { /** * Write a Merkle tree block to the given inode. * - * @inode: the inode for which the Merkle tree is being built + * @req: write request; see struct fsverity_writemerkle * @buf: the Merkle tree block to write * @pos: the position of the block in the Merkle tree (in bytes) * @size: the Merkle tree block size (in bytes) @@ -195,8 +209,9 @@ struct fsverity_operations { * * Return: 0 on success, -errno on failure */ - int (*write_merkle_tree_block)(struct inode *inode, const void *buf, - u64 pos, unsigned int size); + int (*write_merkle_tree_block)(const struct fsverity_writemerkle *req, + const void *buf, u64 pos, + unsigned int size); /** * Release the reference to a Merkle tree block From patchwork Sat Mar 30 00:35:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611293 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E182D1851; Sat, 30 Mar 2024 00:35:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758918; cv=none; b=SgukJtoDGzMNQHK+MaT/vjNd5AApvUCMnHYQrOzxBtssYJgBmMB9aPPPmrg26B7/OeYoNFLUSHCF4c+sjPpiHzxfnvthcBQqHZnFDCAsuIpl5u5If9jJRHpVaFlW+LfbsOC+16mjS379s0o6hvJqpgmMBIwosD6uQKCYZfYxsbE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758918; c=relaxed/simple; bh=JeF+X032QIcM21G487GLEHpX9s+Vv8monq2RZjFwvKM=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=M0R8ihKQBnbvo6xbbWLo5EoCk81OWIzf80NJJcHGeBwmf4+1fJmNQncNTURC9mfJT81hp01Ss5/R+xkjjyoDbtRgWADRiZIS0VisnvPlkyAeSVIkg/iMYgB0GkS1ym55DFf5xk8/354p9uJv0Boo6o0Ds2kAFzaJ2KkDtHQUZTU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=s/xOxpsV; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="s/xOxpsV" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6F82BC433C7; Sat, 30 Mar 2024 00:35:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758917; bh=JeF+X032QIcM21G487GLEHpX9s+Vv8monq2RZjFwvKM=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=s/xOxpsV882/bXluuO/My2TEqaApV/F2m4gbdJLJ96arW1okxT1DgJryzg/JcVzvu sD5esQZ0QynFrxe2ynVsgp+T64dvy5WFWqXv3STg0UdtqcTMSCT8bEu+djWmM6xw9l wY3H97Ts2vRVGA5Ing1zIxym4KCxweZ4fc8mA1AcawMe6KEwRcczvx/Mm2u987gEdF yb6vt66LKi7o6Amvk3KKNgCcmDM9ueZ1Yehl6lbj8UNAIb7bEXD+LflcaeRhfYPHdh 94knypqJOqK7+mHdVTQ9ROtTgdR2S978+WB0scYCtRQ/Cix1bz1gi509tHa65A9jdb prRCtit8dR0Gw== Date: Fri, 29 Mar 2024 17:35:17 -0700 Subject: [PATCH 10/13] fsverity: pass the zero-hash value to the implementation From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175868031.1987804.13138670908694064691.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Compute the hash of a data block full of zeros, and then supply this to the merkle tree read and write methods. A subsequent xfs patch will use this to reduce the size of the merkle tree when dealing with sparse gold master disk images and the like. Signed-off-by: Darrick J. Wong --- fs/verity/enable.c | 2 ++ fs/verity/fsverity_private.h | 2 ++ fs/verity/open.c | 7 +++++++ fs/verity/verify.c | 2 ++ include/linux/fsverity.h | 8 ++++++++ 5 files changed, 21 insertions(+) diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 233b20fb12ff5..8c6fe4b72b14e 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -52,6 +52,8 @@ static int write_merkle_tree_block(struct inode *inode, const u8 *buf, { struct fsverity_writemerkle req = { .inode = inode, + .zero_digest = params->zero_digest, + .digest_size = params->digest_size, }; u64 pos = (u64)index << params->log_blocksize; int err; diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index de8798f141d4a..195a92f203bba 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -47,6 +47,8 @@ struct merkle_tree_params { u64 tree_size; /* Merkle tree size in bytes */ unsigned long tree_pages; /* Merkle tree size in pages */ + u8 zero_digest[FS_VERITY_MAX_DIGEST_SIZE]; /* hash of zeroed data block */ + /* * Starting block index for each tree level, ordered from leaf level (0) * to root level ('num_levels - 1') diff --git a/fs/verity/open.c b/fs/verity/open.c index 7a86407732c41..cdf694a261605 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -144,6 +144,13 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, goto out_err; } + err = fsverity_hash_block(params, inode, page_address(ZERO_PAGE(0)), + params->zero_digest); + if (err) { + fsverity_err(inode, "Error %d computing zero digest", err); + goto out_err; + } + params->tree_size = offset << log_blocksize; params->tree_pages = PAGE_ALIGN(params->tree_size) >> PAGE_SHIFT; return 0; diff --git a/fs/verity/verify.c b/fs/verity/verify.c index c4ebf85ba2c79..99b1529bbb50b 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -432,6 +432,8 @@ int fsverity_read_merkle_tree_block(struct inode *inode, .num_levels = params->num_levels, .log_blocksize = params->log_blocksize, .ra_bytes = ra_bytes, + .zero_digest = params->zero_digest, + .digest_size = params->digest_size, }; block->verified = false; diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 5dacd30d65353..761a0b76eefec 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -66,6 +66,8 @@ struct fsverity_blockbuf { * if the page at @block->offset isn't already cached. * Implementations may ignore this argument; it's only a * performance optimization. + * @zero_digest: the hash for a data block of zeroes + * @digest_size: size of zero_digest */ struct fsverity_readmerkle { struct inode *inode; @@ -73,6 +75,8 @@ struct fsverity_readmerkle { int level; int num_levels; u8 log_blocksize; + const u8 *zero_digest; + unsigned int digest_size; }; /** @@ -81,12 +85,16 @@ struct fsverity_readmerkle { * @level: level of the block; level 0 are the leaves * @num_levels: number of levels in the tree total * @log_blocksize: log2 of the size of the block + * @zero_digest: the hash for a data block of zeroes + * @digest_size: size of zero_digest */ struct fsverity_writemerkle { struct inode *inode; int level; int num_levels; u8 log_blocksize; + const u8 *zero_digest; + unsigned int digest_size; }; /* Verity operations for filesystems */ From patchwork Sat Mar 30 00:35:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611294 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 46919197; Sat, 30 Mar 2024 00:35:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758933; cv=none; b=flAB58JPoH3txcXl/q/oAcP1RLiUcwlOEaaLw+8k6H/33lf2tJlYpTZTW9r3FP9+azk8mOf5LBgt+LFbWZc9MpZ2gUVHrXlZYjKUPqAufLmttvLfVvj1Rr1BbcbfHjIBuWFcZreoK65ywg8FsRK6r2D9m+UZ49tmP7lqIC967QE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758933; c=relaxed/simple; bh=qBzKRn/XNF1X06PWW+d5grtgZ0sPQd5X+SRQ8zwwjKs=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=sZWeIhTPe/l6/Ui62vMxkbOV2e74nR0Eo4xkEnmKjrItqdZUqLX/uoyhze4i7OhrLrZolXYMqaLwzI7MR6LWg6/IxSTJEmyepb4je80yffUc/TPWPUUsZgBuUXB1YGuoyX9K08e57S72FaXh33pZhnHb4Z9OYMBfjx/JlVqKnSA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=M+K6/RtW; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="M+K6/RtW" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 20365C433F1; Sat, 30 Mar 2024 00:35:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758933; bh=qBzKRn/XNF1X06PWW+d5grtgZ0sPQd5X+SRQ8zwwjKs=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=M+K6/RtW1rmNXaWOFslmQE1IFBJEEZgekKJ3yOU+JpbiwlvcobmUfj9grEaqEW6KY 9Yfzc94dK/7Da9ACmXXql5fWI1XYhGRzoGef6LUe2lGd3+sYKj+9ah875lDAIcOXpG gmJpJmVfRPS8qzpbhD31KF+dGuZdkYB5kfZtoneVH/t8S6+oJOKw2a5aVLc2SVJZMQ IEYL7fRDx3XTYgh5d4DWhS5vkKkf1cFfsfeypRgxVC83LeEN3KVQjB3gd4AmDQAuwN litnf1CTyBnBqkKH37BC+XmmpDpfswyllawmgWdCmzEDHvKr/oueohtUhaQg8i0ajf Yo9nMU8A6AiPQ== Date: Fri, 29 Mar 2024 17:35:32 -0700 Subject: [PATCH 11/13] fsverity: report validation errors back to the filesystem From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175868048.1987804.2771715174385554090.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Provide a new function call so that validation errors can be reported back to the filesystem. Signed-off-by: Darrick J. Wong --- fs/verity/verify.c | 14 +++++++++++++- include/linux/fsverity.h | 11 +++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 99b1529bbb50b..4acfd02b0e42d 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -258,6 +258,15 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, return false; } +static void fsverity_fail_validation(struct inode *inode, loff_t pos, + size_t len) +{ + const struct fsverity_operations *vops = inode->i_sb->s_vop; + + if (vops->fail_validation) + vops->fail_validation(inode, pos, len); +} + static bool verify_data_blocks(struct folio *data_folio, size_t len, size_t offset, unsigned long max_ra_bytes) @@ -280,8 +289,11 @@ verify_data_blocks(struct folio *data_folio, size_t len, size_t offset, valid = verify_data_block(inode, vi, data, pos + offset, max_ra_bytes); kunmap_local(data); - if (!valid) + if (!valid) { + fsverity_fail_validation(inode, pos + offset, + block_size); return false; + } offset += block_size; len -= block_size; } while (len); diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 761a0b76eefec..dcf9d9cffcb9f 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -236,6 +236,17 @@ struct fsverity_operations { * be implemented. */ void (*drop_merkle_tree_block)(struct fsverity_blockbuf *block); + + /** + * Notify the filesystem that file data validation failed + * + * @inode: the inode being validated + * @pos: the file position of the invalid data + * @len: the length of the invalid data + * + * This is called when fs-verity cannot validate the file contents. + */ + void (*fail_validation)(struct inode *inode, loff_t pos, size_t len); }; #ifdef CONFIG_FS_VERITY From patchwork Sat Mar 30 00:35:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611295 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0DE1B15A8; Sat, 30 Mar 2024 00:35:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758949; cv=none; b=ReXSZdNJwmpC831a8D/+shSQFJ0A1/2pk95lWN4ST41/mXzWQX94Af/Nvq0edX7MnIqwP0TuW2wm/MlRd/uJG7X5T7sKKa1csWl3wrUeRa8NMd3h+w2e98whEWbW2k0hHLak8YsAs/4vW986mduBaF8ep5yQZs+hxl3InutCwF8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758949; c=relaxed/simple; bh=YD5IkqxojuqXv2orMPlMpmUuWmhiGdvtiuKlINtd5Qg=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=i6/5MYeG10wCIKxII6quFkXY5QeWIWvLaZqURohcXvmjU+kExSGsxf95RfdG02XnsnUD9MHnjDxcrm/xoG8t/LJhmzld0ygvpYHsKbqi/e8ngIDkuH2P12fkeva8nJbf456PbJ9ZvrRD+/mrZLY0LM7sJd5al8ewzNJwutgiu3Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mEcWSwiy; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mEcWSwiy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D089AC433C7; Sat, 30 Mar 2024 00:35:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758948; bh=YD5IkqxojuqXv2orMPlMpmUuWmhiGdvtiuKlINtd5Qg=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=mEcWSwiykw+EMAy7sYL60me+x0An67SPWui6iQyeGXB9aP8XLQSpzhI2i/n/yoIzA HRLFqFuPAYJF4sgtkNaueYl/gQ9o8jE0m86U5uXH3OO9KYw3gR3pA7SBCUXI9ty9Vk BMXtNBuigYiOB/c/eWi8ftUwE/o9/YBagBMNpKvV+rAgSclxnFAxDeJtjZEbmpwx1/ 5nI1O1Po0rKTad6Vi3/FrIfKTnPkBLUczPgh5zuL42CzhZybbBKw+jUwprZtjQKddD AVhdH53+7rKISRxMR1oeKGyqO0sneV09xKluW28GhTYamYTXfIZc/ej+YHW5g+Ko5G eo6Z6v96SJBdA== Date: Fri, 29 Mar 2024 17:35:48 -0700 Subject: [PATCH 12/13] fsverity: remove system-wide workqueue From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175868064.1987804.7068231057141413548.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Now that we've made the verity workqueue per-superblock, we don't need the systemwide workqueue. Get rid of the old implementation. Signed-off-by: Darrick J. Wong --- fs/btrfs/super.c | 6 ++++++ fs/buffer.c | 7 +++++-- fs/ext4/readpage.c | 5 ++++- fs/ext4/super.c | 3 +++ fs/f2fs/compress.c | 3 ++- fs/f2fs/data.c | 2 +- fs/f2fs/super.c | 3 +++ fs/verity/fsverity_private.h | 2 -- fs/verity/init.c | 1 - fs/verity/verify.c | 25 ++++--------------------- include/linux/fsverity.h | 6 ++++-- 11 files changed, 32 insertions(+), 31 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 7e44ccaf348f2..c2da9a0009e26 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "messages.h" #include "delayed-inode.h" #include "ctree.h" @@ -924,6 +925,11 @@ static int btrfs_fill_super(struct super_block *sb, sb->s_export_op = &btrfs_export_ops; #ifdef CONFIG_FS_VERITY sb->s_vop = &btrfs_verityops; + err = fsverity_init_verify_wq(sb); + if (err) { + btrfs_err(fs_info, "fsverity_init_verify_wq failed"); + return err; + } #endif sb->s_xattr = btrfs_xattr_handlers; sb->s_time_gran = 1; diff --git a/fs/buffer.c b/fs/buffer.c index 4f73d23c2c469..a2cb50ecfb829 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -327,13 +327,15 @@ static void decrypt_bh(struct work_struct *work) err = fscrypt_decrypt_pagecache_blocks(bh->b_folio, bh->b_size, bh_offset(bh)); if (err == 0 && need_fsverity(bh)) { + struct inode *inode = bh->b_folio->mapping->host; + /* * We use different work queues for decryption and for verity * because verity may require reading metadata pages that need * decryption, and we shouldn't recurse to the same workqueue. */ INIT_WORK(&ctx->work, verify_bh); - fsverity_enqueue_verify_work(&ctx->work); + fsverity_enqueue_verify_work(inode->i_sb, &ctx->work); return; } end_buffer_async_read(bh, err == 0); @@ -362,7 +364,8 @@ static void end_buffer_async_read_io(struct buffer_head *bh, int uptodate) fscrypt_enqueue_decrypt_work(&ctx->work); } else { INIT_WORK(&ctx->work, verify_bh); - fsverity_enqueue_verify_work(&ctx->work); + fsverity_enqueue_verify_work(inode->i_sb, + &ctx->work); } return; } diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 21e8f0aebb3c6..e40566b0ddb65 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -61,6 +61,7 @@ enum bio_post_read_step { struct bio_post_read_ctx { struct bio *bio; + const struct inode *inode; struct work_struct work; unsigned int cur_step; unsigned int enabled_steps; @@ -132,7 +133,8 @@ static void bio_post_read_processing(struct bio_post_read_ctx *ctx) case STEP_VERITY: if (ctx->enabled_steps & (1 << STEP_VERITY)) { INIT_WORK(&ctx->work, verity_work); - fsverity_enqueue_verify_work(&ctx->work); + fsverity_enqueue_verify_work(ctx->inode->i_sb, + &ctx->work); return; } ctx->cur_step++; @@ -195,6 +197,7 @@ static void ext4_set_bio_post_read_ctx(struct bio *bio, mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS); ctx->bio = bio; + ctx->inode = inode; ctx->enabled_steps = post_read_steps; bio->bi_private = ctx; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index cfb8449c731f9..f7c834f4e2b1f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -5332,6 +5332,9 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) #endif #ifdef CONFIG_FS_VERITY sb->s_vop = &ext4_verityops; + err = fsverity_init_verify_wq(sb); + if (err) + goto failed_mount3a; #endif #ifdef CONFIG_QUOTA sb->dq_op = &ext4_quota_operations; diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 8892c82621414..efd0b0a3a2c37 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1775,7 +1775,8 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed, * file, and these metadata pages may be compressed. */ INIT_WORK(&dic->verity_work, f2fs_verify_cluster); - fsverity_enqueue_verify_work(&dic->verity_work); + fsverity_enqueue_verify_work(dic->inode->i_sb, + &dic->verity_work); return; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d9494b5fc7c18..994339216a06e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -221,7 +221,7 @@ static void f2fs_verify_and_finish_bio(struct bio *bio, bool in_task) if (ctx && (ctx->enabled_steps & STEP_VERITY)) { INIT_WORK(&ctx->work, f2fs_verify_bio); - fsverity_enqueue_verify_work(&ctx->work); + fsverity_enqueue_verify_work(ctx->sbi->sb, &ctx->work); } else { f2fs_finish_read_bio(bio, in_task); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a6867f26f1418..422527d9f6baf 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4423,6 +4423,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) #endif #ifdef CONFIG_FS_VERITY sb->s_vop = &f2fs_verityops; + err = fsverity_init_verify_wq(sb); + if (err) + goto free_bio_info; #endif sb->s_xattr = f2fs_xattr_handlers; sb->s_export_op = &f2fs_export_ops; diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index 195a92f203bba..24846d475502d 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -154,8 +154,6 @@ static inline void fsverity_init_signature(void) /* verify.c */ -void __init fsverity_init_workqueue(void); - static inline bool fsverity_caches_blocks(const struct inode *inode) { const struct fsverity_operations *vops = inode->i_sb->s_vop; diff --git a/fs/verity/init.c b/fs/verity/init.c index 3769d2dc9e3b4..4663696c6996c 100644 --- a/fs/verity/init.c +++ b/fs/verity/init.c @@ -66,7 +66,6 @@ static int __init fsverity_init(void) { fsverity_check_hash_algs(); fsverity_init_info_cache(); - fsverity_init_workqueue(); fsverity_init_sysctl(); fsverity_init_signature(); fsverity_init_bpf(); diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 4acfd02b0e42d..99c6d31fbcfba 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -10,8 +10,6 @@ #include #include -static struct workqueue_struct *fsverity_read_workqueue; - /* * Returns true if the hash block with index @hblock_idx in the tree has * already been verified. @@ -384,33 +382,18 @@ EXPORT_SYMBOL_GPL(__fsverity_init_verify_wq); /** * fsverity_enqueue_verify_work() - enqueue work on the fs-verity workqueue + * @sb: superblock for this filesystem * @work: the work to enqueue * * Enqueue verification work for asynchronous processing. */ -void fsverity_enqueue_verify_work(struct work_struct *work) +void fsverity_enqueue_verify_work(struct super_block *sb, + struct work_struct *work) { - queue_work(fsverity_read_workqueue, work); + queue_work(sb->s_verify_wq, work); } EXPORT_SYMBOL_GPL(fsverity_enqueue_verify_work); -void __init fsverity_init_workqueue(void) -{ - /* - * Use a high-priority workqueue to prioritize verification work, which - * blocks reads from completing, over regular application tasks. - * - * For performance reasons, don't use an unbound workqueue. Using an - * unbound workqueue for crypto operations causes excessive scheduler - * latency on ARM64. - */ - fsverity_read_workqueue = alloc_workqueue("fsverity_read_queue", - WQ_HIGHPRI, - num_online_cpus()); - if (!fsverity_read_workqueue) - panic("failed to allocate fsverity_read_queue"); -} - /** * fsverity_read_merkle_tree_block() - read Merkle tree block * @inode: inode to which this Merkle tree blocks belong diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index dcf9d9cffcb9f..ed93ca06ade5f 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -302,7 +302,8 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg); bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset); void fsverity_verify_bio(struct bio *bio); -void fsverity_enqueue_verify_work(struct work_struct *work); +void fsverity_enqueue_verify_work(struct super_block *sb, + struct work_struct *work); int __fsverity_init_verify_wq(struct super_block *sb); static inline int fsverity_init_verify_wq(struct super_block *sb) @@ -384,7 +385,8 @@ static inline void fsverity_verify_bio(struct bio *bio) WARN_ON_ONCE(1); } -static inline void fsverity_enqueue_verify_work(struct work_struct *work) +static inline void fsverity_enqueue_verify_work(struct super_block *sb, + struct work_struct *work) { WARN_ON_ONCE(1); } From patchwork Sat Mar 30 00:36:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13611296 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD0981851; Sat, 30 Mar 2024 00:36:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758965; cv=none; b=lt8cfPCXJogpqZbGhLkvyhReoAackgbOFy+/51H3azhiEO50cXjuTNZkzayVMHykBHHdrjQpj0cS3UAMNwS670Tfi6vhYsYl1LvISHwK+69Fm6IQ0QoK0IuH41CNdJxj0ZpZJUfQDGmwNVXbLi6cQjAbgPEE5KacK0D3uYPXw74= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711758965; c=relaxed/simple; bh=oQH8xAJ/JADHGK4A2NMf6GJLSuDZm9k61AYIFHuvRNE=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=tvqkARIryF4a0QcZEaU/amQ8S/4lwz3IQgggJe/s3bI9C+w3ijwXG/mo2Y3XFpq2oAf33RVYHpI4PaTQS50/PrpJR9A79S8NZDSpnmGMDWSZ2uzN8sHdtbgi3r/dI/kjVyUbD6HDILdjB9/fUq76m3mK/vCq4Afu59CWfspzC5Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DRq/waav; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DRq/waav" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7A56BC433F1; Sat, 30 Mar 2024 00:36:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1711758964; bh=oQH8xAJ/JADHGK4A2NMf6GJLSuDZm9k61AYIFHuvRNE=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=DRq/waavT46kt61YHN4RTa82J62A681ipWATWf5jD0n1okurI1QC35zRplgbC9x3Y XxunxRXIpAwiOcJoxVd8Z+uc8cwXVW3hHTkRb8lLMZ6eUnqxDw21tPdDzGQXkfDnXn 1U0iwa6XHvsXgvkXTnqBVq+aPskRLEw+2o0pPufU4HL6qsuj+qa6CUrjoPFlxJcxrd e9p0Hk1S838plvkTCImMR930WB1hNqjF7gf1CtBvqimMci/PFLh3s9A2gjSS2I52ky Nbb9fuJXMNBUBPCUiF3wpPxeucGLU7sqrQEXz3xTJFI+DO1aIxK9E/JZXPYCf+4V3M WAc7Wktk/oRuw== Date: Fri, 29 Mar 2024 17:36:04 -0700 Subject: [PATCH 13/13] iomap: integrate fs-verity verification into iomap's read path From: "Darrick J. Wong" To: djwong@kernel.org, ebiggers@kernel.org, aalbersh@redhat.com Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, fsverity@lists.linux.dev Message-ID: <171175868082.1987804.4008377696050953236.stgit@frogsfrogsfrogs> In-Reply-To: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> References: <171175867829.1987804.15934006844321506283.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Andrey Albershteyn This patch adds fs-verity verification into iomap's read path. After BIO's io operation is complete the data are verified against fs-verity's Merkle tree. Verification work is done in a separate workqueue. The read path ioend iomap_read_ioend are stored side by side with BIOs if FS_VERITY is enabled. Signed-off-by: Andrey Albershteyn Signed-off-by: Christoph Hellwig Reviewed-by: Darrick J. Wong [djwong: fix doc warning] Signed-off-by: Darrick J. Wong --- fs/iomap/buffered-io.c | 128 +++++++++++++++++++++++++++++++++++++++++++++--- include/linux/iomap.h | 4 ++ 2 files changed, 125 insertions(+), 7 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 4e8e41c8b3c0e..9f9d929dfeebc 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,8 @@ #define IOEND_BATCH_SIZE 4096 +#define IOMAP_POOL_SIZE (4 * (PAGE_SIZE / SECTOR_SIZE)) + typedef int (*iomap_punch_t)(struct inode *inode, loff_t offset, loff_t length); /* * Structure allocated for each folio to track per-block uptodate, dirty state @@ -368,6 +371,110 @@ static inline bool iomap_block_needs_zeroing(const struct iomap_iter *iter, pos >= i_size_read(iter->inode); } +#ifdef CONFIG_FS_VERITY +struct iomap_fsverity_bio { + struct work_struct work; + struct bio bio; +}; +static struct bio_set *iomap_fsverity_bioset; + +static int iomap_fsverity_init_bioset(void) +{ + struct bio_set *bs, *old; + int error; + + bs = kzalloc(sizeof(*bs), GFP_KERNEL); + if (!bs) + return -ENOMEM; + + error = bioset_init(bs, IOMAP_POOL_SIZE, + offsetof(struct iomap_fsverity_bio, bio), + BIOSET_NEED_BVECS); + if (error) { + kfree(bs); + return error; + } + + /* + * This has to be atomic as readaheads can race to create the + * bioset. If someone set the pointer before us, we drop ours. + */ + old = cmpxchg(&iomap_fsverity_bioset, NULL, bs); + if (old) { + bioset_exit(bs); + kfree(bs); + } + + return 0; +} + +int iomap_init_fsverity(struct super_block *sb) +{ + int ret; + + if (!iomap_fsverity_bioset) { + ret = iomap_fsverity_init_bioset(); + if (ret) + return ret; + } + + return fsverity_init_verify_wq(sb); +} +EXPORT_SYMBOL_GPL(iomap_init_fsverity); + +static void +iomap_read_fsverify_end_io_work(struct work_struct *work) +{ + struct iomap_fsverity_bio *fbio = + container_of(work, struct iomap_fsverity_bio, work); + + fsverity_verify_bio(&fbio->bio); + iomap_read_end_io(&fbio->bio); +} + +static void +iomap_read_fsverity_end_io(struct bio *bio) +{ + struct iomap_fsverity_bio *fbio = + container_of(bio, struct iomap_fsverity_bio, bio); + + INIT_WORK(&fbio->work, iomap_read_fsverify_end_io_work); + queue_work(bio->bi_private, &fbio->work); +} + +static struct bio * +iomap_fsverity_read_bio_alloc(struct inode *inode, struct block_device *bdev, + int nr_vecs, gfp_t gfp) +{ + struct bio *bio; + + bio = bio_alloc_bioset(bdev, nr_vecs, REQ_OP_READ, gfp, + iomap_fsverity_bioset); + if (bio) { + bio->bi_private = inode->i_sb->s_verify_wq; + bio->bi_end_io = iomap_read_fsverity_end_io; + } + return bio; +} +#else +# define iomap_fsverity_read_bio_alloc(...) (NULL) +# define iomap_fsverity_init_bioset(...) (-EOPNOTSUPP) +#endif /* CONFIG_FS_VERITY */ + +static struct bio *iomap_read_bio_alloc(struct inode *inode, + struct block_device *bdev, int nr_vecs, gfp_t gfp) +{ + struct bio *bio; + + if (fsverity_active(inode)) + return iomap_fsverity_read_bio_alloc(inode, bdev, nr_vecs, gfp); + + bio = bio_alloc(bdev, nr_vecs, REQ_OP_READ, gfp); + if (bio) + bio->bi_end_io = iomap_read_end_io; + return bio; +} + static loff_t iomap_readpage_iter(const struct iomap_iter *iter, struct iomap_readpage_ctx *ctx, loff_t offset) { @@ -391,6 +498,12 @@ static loff_t iomap_readpage_iter(const struct iomap_iter *iter, if (iomap_block_needs_zeroing(iter, pos)) { folio_zero_range(folio, poff, plen); + if (fsverity_active(iter->inode) && + !fsverity_verify_blocks(folio, plen, poff)) { + folio_set_error(folio); + goto done; + } + iomap_set_range_uptodate(folio, poff, plen); goto done; } @@ -408,28 +521,29 @@ static loff_t iomap_readpage_iter(const struct iomap_iter *iter, !bio_add_folio(ctx->bio, folio, plen, poff)) { gfp_t gfp = mapping_gfp_constraint(folio->mapping, GFP_KERNEL); gfp_t orig_gfp = gfp; - unsigned int nr_vecs = DIV_ROUND_UP(length, PAGE_SIZE); if (ctx->bio) submit_bio(ctx->bio); if (ctx->rac) /* same as readahead_gfp_mask */ gfp |= __GFP_NORETRY | __GFP_NOWARN; - ctx->bio = bio_alloc(iomap->bdev, bio_max_segs(nr_vecs), - REQ_OP_READ, gfp); + + ctx->bio = iomap_read_bio_alloc(iter->inode, iomap->bdev, + bio_max_segs(DIV_ROUND_UP(length, PAGE_SIZE)), + gfp); + /* * If the bio_alloc fails, try it again for a single page to * avoid having to deal with partial page reads. This emulates * what do_mpage_read_folio does. */ if (!ctx->bio) { - ctx->bio = bio_alloc(iomap->bdev, 1, REQ_OP_READ, - orig_gfp); + ctx->bio = iomap_read_bio_alloc(iter->inode, + iomap->bdev, 1, orig_gfp); } if (ctx->rac) ctx->bio->bi_opf |= REQ_RAHEAD; ctx->bio->bi_iter.bi_sector = sector; - ctx->bio->bi_end_io = iomap_read_end_io; bio_add_folio_nofail(ctx->bio, folio, plen, poff); } @@ -1987,7 +2101,7 @@ EXPORT_SYMBOL_GPL(iomap_writepages); static int __init iomap_init(void) { - return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE), + return bioset_init(&iomap_ioend_bioset, IOMAP_POOL_SIZE, offsetof(struct iomap_ioend, io_bio), BIOSET_NEED_BVECS); } diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 6fc1c858013d1..d486ec62d4082 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -256,6 +256,10 @@ static inline const struct iomap *iomap_iter_srcmap(const struct iomap_iter *i) return &i->iomap; } +#ifdef CONFIG_FS_VERITY +int iomap_init_fsverity(struct super_block *sb); +#endif + ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from, const struct iomap_ops *ops); int iomap_file_buffered_write_punch_delalloc(struct inode *inode,