All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Andrey Albershteyn" <aalbersh@kernel.org>,
	ebiggers@kernel.org, hch@lst.de,
	"Carlos Maiolino" <cem@kernel.org>,
	fsverity@lists.linux.dev
Cc: david@fromorbit.com, "Darrick J. Wong" <djwong@kernel.org>
Subject: Re: [PATCH v18 08/15] xfs: don't remove written extents past EOF on fsverity inodes
Date: Mon, 02 Nov 2026 15:11:10 +0000	[thread overview]
Message-ID: <sashiko-outbox-193639@kernel.org> (raw)
In-Reply-To: <20261102144147.2393369-9-aalbersh@kernel.org>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] xfs: written verity extents unmapped in xfs_free_eofblocks() on bigrtalloc
- [Medium] xfs: fsverity_active() check in xfs_can_free_eofblocks() fails if !VERITY
- [Medium] xfs: premature XFS_IEOFBLOCKS clear in xfs_can_free_eofblocks()
--

commit 2307cc3d64e773ab52b2cf76f7c9b88f364d2957
Author: Andrey Albershteyn <aalbersh@kernel.org>

xfs: don't remove written extents past EOF on fsverity inodes

This commit updates xfs_can_free_eofblocks() to skip active fsverity inodes
and inodes undergoing Merkle tree construction, and modifies
xfs_free_eofblocks() to punch only delalloc and unwritten extents past EOF on
fsverity inodes while preserving written metadata extents.

> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index 865aec3ae5ab3..32cec523fd52d 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
> @@ -31,6 +31,7 @@
>  #include "xfs_rtbitmap.h"
>  #include "xfs_rtgroup.h"
>  #include "xfs_zone_alloc.h"
> +#include <linux/fsverity.h>
>  
>  /* Kernel only BMAP related definitions and functions */
>  
> @@ -553,6 +554,15 @@ xfs_can_free_eofblocks(
>  	if (last_fsb <= end_fsb)
>  		return false;
>  
> +	/*
> +	 * Don't clean fsverity inodes as they already have been cleaned up and
> +	 * are read-only. Skip inodes which have merkle tree being built, the
> +	 * merkle tree is written beyond EOF
> +	 */
> +	if (fsverity_active(VFS_IC(ip)) ||
> +	    xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> +		return false;

[Severity: Medium]
As reported by Sashiko on v16 [1], in xfs_can_free_eofblocks() in
fs/xfs/xfs_bmap_util.c, does checking fsverity_active(VFS_IC(ip)) instead of
ip->i_diflags2 & XFS_DIFLAG2_VERITY cause sealed fsverity inodes to go
through unnecessary inactivation and tr_itruncate transactions on every VFS
cache eviction when CONFIG_FS_VERITY=n?

The follow-up commit ("xfs: enable ro-compat fs-verity flag") unconditionally
includes XFS_SB_FEAT_RO_COMPAT_VERITY in XFS_SB_FEAT_RO_COMPAT_ALL, allowing
read-write mounts of fsverity filesystems even on kernels built with
CONFIG_FS_VERITY=n. When CONFIG_FS_VERITY=n, fsverity_active() in
include/linux/fsverity.h is a stub returning false:

    static inline bool fsverity_active(const struct inode *inode)
    {
        return false;
    }

Whereas xfs_free_eofblocks() checks:

    bool has_verity = ip->i_diflags2 & XFS_DIFLAG2_VERITY;

When a sealed extent-format fsverity inode (with written Merkle tree and
descriptor extents past EOF) is looked up and later evicted on a
CONFIG_FS_VERITY=n kernel:

    xfs_inode_mark_reclaimable()
      -> xfs_inode_needs_inactive()
        -> xfs_can_free_eofblocks()

xfs_can_free_eofblocks() sees fsverity_active() == false, finds the post-EOF
Merkle tree extents via xfs_iext_lookup_extent(), and returns true. This
queues the inode for background inactivation, where xfs_inactive() ->
xfs_free_eofblocks() sees has_verity == true, allocates a tr_itruncate
transaction, walks all post-EOF extents with XFS_BMAPI_UNWRITTEN without
freeing the written Merkle tree extents, logs XFS_ILOG_CORE in
xfs_itruncate_extents_flags(), and commits a dirty transaction on every
eviction.

Should xfs_can_free_eofblocks() check ip->i_diflags2 & XFS_DIFLAG2_VERITY (or
IS_VERITY(VFS_IC(ip))) to match xfs_free_eofblocks()?

[Severity: Medium]
Also in xfs_can_free_eofblocks() in fs/xfs/xfs_bmap_util.c, can returning
false when XFS_VERITY_CONSTRUCTION is set cause xfs_inode_free_eofblocks() to
prematurely clear the XFS_IEOFBLOCKS tag and leak post-EOF speculative
preallocations if Merkle tree construction later fails?

During FS_IOC_ENABLE_VERITY, enable_verity() in fs/verity/enable.c acquires
inode_lock(inode) (XFS_IOLOCK_EXCL), calls ->begin_enable_verity() which sets
XFS_VERITY_CONSTRUCTION, and then drops inode_lock(inode) while calling
build_merkle_tree(). While build_merkle_tree() writes past EOF without
holding XFS_IOLOCK_EXCL:

    xfs_fsverity_write_merkle()
      -> iomap_fsverity_write()
        -> xfs_buffered_write_iomap_begin()
          -> xfs_bmapi_reserve_delalloc()
            -> xfs_inode_set_eofblocks_tag()

If background xfs_blockgc_worker() or a synchronous -ENOSPC/-EDQUOT scan runs
xfs_inode_free_eofblocks() in fs/xfs/xfs_icache.c while
XFS_VERITY_CONSTRUCTION is set, xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)
succeeds and xfs_can_free_eofblocks(ip) returns false:

    if (xfs_can_free_eofblocks(ip))
        return xfs_free_eofblocks(ip);

    /* inode could be preallocated */
    trace_xfs_inode_free_eofblocks_invalid(ip);
    xfs_inode_clear_eofblocks_tag(ip);
    return 0;

This removes the inode from the per-AG XFS_ICI_BLOCKGC_TAG radix tree while
post-EOF delalloc and speculative preallocation blocks are still attached. If
build_merkle_tree() subsequently fails and enable_verity() invokes the
rollback path ->end_enable_verity(filp, NULL, 0), xfs_fsverity_end_enable()
jumps to out with error == 0 and clears XFS_VERITY_CONSTRUCTION without
calling xfs_free_eofblocks() or xfs_fsverity_reset_inode(). Because the file
descriptor for FS_IOC_ENABLE_VERITY is opened O_RDONLY, xfs_release() does
not free post-EOF blocks on close either, leaving the post-EOF blocks
untracked by blockgc until inode eviction.

>  
>  	/*
>  	 * Check if there is an post-EOF extent to free.  If there are any
> @@ -579,6 +589,9 @@ xfs_free_eofblocks(
>  	struct xfs_trans	*tp;
>  	struct xfs_mount	*mp = ip->i_mount;
>  	int			error;
> +	int			bmapi_flags = XFS_BMAPI_NODISCARD;
> +	bool			has_verity =
> +			ip->i_diflags2 & XFS_DIFLAG2_VERITY;
>  
>  	/* Attach the dquots to the inode up front. */
>  	error = xfs_qm_dqattach(ip);
> @@ -593,15 +606,20 @@ xfs_free_eofblocks(
>  	 *
>  	 * Note that this means we also leave speculative preallocations in
>  	 * place for preallocated files.
> +	 *
> +	 * Clean up delalloc reservations for fsverity too as those won't be
> +	 * used
>  	 */
> -	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
> +	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND) ||
> +			has_verity) {
>  		if (ip->i_delayed_blks) {
>  			xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK,
>  				round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
>  				LLONG_MAX, NULL);
>  		}
>  		xfs_inode_clear_eofblocks_tag(ip);
> -		return 0;
> +		if (!has_verity)
> +			return 0;
>  	}
>  
>  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> @@ -613,6 +631,13 @@ xfs_free_eofblocks(
>  	xfs_ilock(ip, XFS_ILOCK_EXCL);
>  	xfs_trans_ijoin(tp, ip, 0);
>  
> +	/*
> +	 * While fs-verity writes metadata after EOF, it can leave unwritten
> +	 * preallocations.  Clear all that out.
> +	 */
> +	if (has_verity)
> +		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
> +
>  	/*
>  	 * Do not update the on-disk file size.  If we update the on-disk file
>  	 * size and then the system crashes before the contents of the file are
> @@ -620,7 +645,7 @@ xfs_free_eofblocks(
>  	 * bug).
>  	 */
>  	error = xfs_itruncate_extents_flags(&tp, ip, XFS_DATA_FORK,
> -				XFS_ISIZE(ip), XFS_BMAPI_NODISCARD);
> +				XFS_ISIZE(ip), bmapi_flags);

[Severity: High]
In xfs_free_eofblocks() in fs/xfs/xfs_bmap_util.c, if an inode has both
XFS_DIFLAG2_VERITY and bigrtalloc (xfs_inode_has_bigrtalloc(ip)), can calling
xfs_itruncate_extents_flags() with XFS_BMAPI_UNWRITTEN and a 0-block
transaction reservation convert written post-EOF fsverity metadata blocks to
unwritten and unmap them (or trigger an -ENOSPC filesystem shutdown)?

Although xfs_fsverity_begin_enable() in the follow-up patch ("xfs: add
fs-verity support") checks xfs_inode_has_bigrtalloc(ip), enable_verity() in
fs/verity/enable.c drops inode_lock(inode) immediately after
->begin_enable_verity() returns. While inode_lock(inode) is dropped on a file
with no allocated data blocks yet (where xfs_inode_has_filedata(ip) is false,
such as an empty or hole-only file before Merkle tree blocks are allocated), a
concurrent FS_IOC_FSSETXATTR / file_setattr() call reaches:

    xfs_fileattr_set()
      -> xfs_ioctl_setattr_xflags()

which does not check XFS_VERITY_CONSTRUCTION or XFS_DIFLAG2_VERITY and allows
setting XFS_DIFLAG_REALTIME on the inode. In addition, xfs_dinode_verify() in
fs/xfs/libxfs/xfs_inode_buf.c does not reject on-disk inodes with both
XFS_DIFLAG2_VERITY and bigrtalloc.

When xfs_fsverity_end_enable() writes the fsverity descriptor on a bigrtalloc
inode (sb_rextsize > 1), xfs_buffered_write_iomap_begin() delegates via
xfs_get_extsz_hint() to xfs_direct_write_iomap_begin(), preallocating an
sb_rextsize-aligned unwritten realtime extent and converting only the
written descriptor block(s) to XFS_EXT_NORM, leaving an XFS_EXT_UNWRITTEN
tail in the same realtime extent.

When xfs_free_eofblocks() then calls xfs_itruncate_extents_flags() with
XFS_BMAPI_UNWRITTEN and tp->t_blk_res == 0, __xfs_bunmapi() in
fs/xfs/libxfs/xfs_bmap.c hits the unwritten tail of that realtime extent
(where del.br_state == XFS_EXT_UNWRITTEN, aligned at the end of the realtime
extent, but with mod = xfs_rtb_to_rtxoff(mp, del.br_startblock) != 0 and
del.br_startoff > start):

    } else if (del.br_state == XFS_EXT_UNWRITTEN) {
        struct xfs_bmbt_irec prev;
        xfs_fileoff_t unwrite_start;

        /*
         * This one is already unwritten.
         * It must have a written left neighbor.
         * Unwrite the killed part of that one and
         * try again.
         */
        if (!xfs_iext_prev_extent(ifp, &icur, &prev))
            ASSERT(0);
        ASSERT(prev.br_state == XFS_EXT_NORM);

__xfs_bunmapi() converts the written left neighbor prev (containing the
written fsverity metadata) to XFS_EXT_UNWRITTEN via
xfs_bmap_add_extent_unwritten_real() without any block reservation for a bmap
btree split, and then unmaps and frees the entire realtime extent on the next
iteration.

[1] https://lore.kernel.org/r/20260918111539.1003439-15-aalbersh@kernel.org

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102144147.2393369-1-aalbersh@kernel.org?part=8

  reply	other threads:[~2026-11-02 15:11 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 14:41 [PATCH v18 00/15] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
2026-11-02 14:41 ` [PATCH v18 01/15] xfs: introduce fsverity on-disk changes Andrey Albershteyn
2026-11-02 15:08   ` sashiko-bot
2026-11-02 14:41 ` [PATCH v18 02/15] xfs: don't allow to enable DAX on fs-verity sealed inode Andrey Albershteyn
2026-11-02 14:41 ` [PATCH v18 03/15] xfs: disable direct read path for fs-verity files Andrey Albershteyn
2026-11-02 14:41 ` [PATCH v18 04/15] xfs: don't report dio_mem_align and dio_offset_align for fsverity files Andrey Albershteyn
2026-11-03  9:23   ` Christoph Hellwig
2026-11-02 14:41 ` [PATCH v18 05/15] xfs: handle fsverity I/O in write/read path Andrey Albershteyn
2026-11-02 15:13   ` sashiko-bot
2026-11-03  9:37   ` Christoph Hellwig
2026-11-03 13:55     ` Andrey Albershteyn
2026-11-03 14:53       ` Christoph Hellwig
2026-11-03 15:26         ` Andrey Albershteyn
2026-11-03 22:03         ` Eric Biggers
2026-11-02 14:41 ` [PATCH v18 06/15] xfs: use read ioend for fsverity data verification Andrey Albershteyn
2026-11-02 14:57   ` sashiko-bot
2026-11-03  9:39   ` Christoph Hellwig
2026-11-02 14:41 ` [PATCH v18 07/15] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi() Andrey Albershteyn
2026-11-02 15:02   ` sashiko-bot
2026-11-03  9:41   ` Christoph Hellwig
2026-11-03 14:31     ` Andrey Albershteyn
2026-11-03 14:39       ` Christoph Hellwig
2026-11-03 14:45         ` Andrey Albershteyn
2026-11-03 14:49           ` Christoph Hellwig
2026-11-03 15:17             ` Andrey Albershteyn
2026-11-02 14:41 ` [PATCH v18 08/15] xfs: don't remove written extents past EOF on fsverity inodes Andrey Albershteyn
2026-11-02 15:11   ` sashiko-bot [this message]
2026-11-03  9:42   ` Christoph Hellwig
2026-11-02 14:41 ` [PATCH v18 09/15] xfs: add fs-verity support Andrey Albershteyn
2026-11-02 15:34   ` sashiko-bot
2026-11-03 18:23     ` Eric Biggers
2026-11-02 14:41 ` [PATCH v18 10/15] xfs: initialize fs-verity on file open Andrey Albershteyn
2026-11-02 14:41 ` [PATCH v18 11/15] xfs: add fs-verity ioctls Andrey Albershteyn
2026-11-02 15:24   ` sashiko-bot
2026-11-02 14:41 ` [PATCH v18 12/15] xfs: advertise fs-verity being available on filesystem Andrey Albershteyn
2026-11-02 14:41 ` [PATCH v18 13/15] xfs: check and repair the verity inode flag state Andrey Albershteyn
2026-11-02 15:28   ` sashiko-bot
2026-11-02 14:41 ` [PATCH v18 14/15] xfs: introduce health state for corrupted fsverity metadata Andrey Albershteyn
2026-11-02 15:16   ` sashiko-bot
2026-11-02 14:41 ` [PATCH v18 15/15] xfs: enable ro-compat fs-verity flag Andrey Albershteyn
2026-11-02 15:35   ` sashiko-bot
2026-11-03  9:44   ` Christoph Hellwig

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=sashiko-outbox-193639@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=aalbersh@kernel.org \
    --cc=cem@kernel.org \
    --cc=david@fromorbit.com \
    --cc=djwong@kernel.org \
    --cc=ebiggers@kernel.org \
    --cc=fsverity@lists.linux.dev \
    --cc=hch@lst.de \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

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

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.