From: Heming Zhao <heming.zhao@suse.com>
To: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
Mark Fasheh <mark@fasheh.com>, Joel Becker <jlbec@evilplan.org>,
ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] ocfs2: validate the xattr tree root of an indexed xattr block
Date: Wed, 14 Oct 2026 22:35:29 +0800 [thread overview]
Message-ID: <as-SUraepqsded7n@p15> (raw)
In-Reply-To: <20261014105856.2909777-1-joseph.qi@linux.alibaba.com>
On Wed, Oct 14, 2026 at 06:58:56PM +0800, Joseph Qi wrote:
> ocfs2_validate_xattr_block() checks an xattr block's signature, blkno,
> generation and suballocator fields, and for a non-indexed block the entry
> array it carries. An indexed block is passed straight through, so the
> extent list in its xattr tree root is never looked at.
>
> Both fields that describe the list are bounded. The two places that
> create a root set l_count from ocfs2_xattr_recs_per_xb() and nothing
> rewrites it, and the insert path asserts in ocfs2_rotate_leaf() that
> l_next_free_rec stays within l_count. Check both when the block is
> first read, so a root that disagrees with the block size, or claims more
> records than it has room for, is caught before anything indexes into
> l_recs[].
>
> An empty root is deliberately not rejected. Emptying the tree leaves it
> that way: ocfs2_rm_xattr_cluster() removes one extent per transaction,
> and the last one ends in ocfs2_truncate_rec(), whose closing
> ocfs2_rotate_tree_left() reaches ocfs2_remove_empty_extent() on the
> depth-zero root and takes l_next_free_rec to zero. That transaction
> commits before ocfs2_xattr_free_block() frees the block, and before
> ocfs2_xattr_remove() clears i_xattr_loc, in two later transactions, so a
> crash in between leaves an orphan inode pointing at an xattr block whose
> root has no extents. Both readers already cope with it:
> __ocfs2_xattr_index_block_find() answers -ENODATA, and
> ocfs2_iterate_xattr_index_block() returns without iterating.
>
> Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
LGTM.
Reviewed-by: Heming Zhao <heming.zhao@suse.com>
> ---
> fs/ocfs2/xattr.c | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index 18d920b30602..b2bd06083a14 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -491,6 +491,7 @@ static int ocfs2_validate_xattr_block(struct super_block *sb,
> struct buffer_head *bh)
> {
> int rc;
> + struct ocfs2_extent_list *el;
> struct ocfs2_xattr_block *xb =
> (struct ocfs2_xattr_block *)bh->b_data;
>
> @@ -573,6 +574,30 @@ static int ocfs2_validate_xattr_block(struct super_block *sb,
> region_offset);
> }
>
> + /*
> + * An indexed xattr block carries the root of the xattr tree. Its
> + * record count is fixed by the block size and l_next_free_rec never
> + * exceeds it. A zero l_next_free_rec is valid: emptying the tree on
> + * the inode deletion path leaves the root with no extents.
> + */
> + el = &xb->xb_attrs.xb_root.xt_list;
> +
> + if (le16_to_cpu(el->l_count) != ocfs2_xattr_recs_per_xb(sb)) {
> + return ocfs2_error(sb,
> + "Xattr tree root #%llu has invalid l_count %u (expected %u)\n",
> + (unsigned long long)bh->b_blocknr,
> + le16_to_cpu(el->l_count),
> + ocfs2_xattr_recs_per_xb(sb));
> + }
> +
> + if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) {
> + return ocfs2_error(sb,
> + "Xattr tree root #%llu has l_next_free_rec %u past the end of its list (l_count %u)\n",
> + (unsigned long long)bh->b_blocknr,
> + le16_to_cpu(el->l_next_free_rec),
> + le16_to_cpu(el->l_count));
> + }
> +
> return 0;
> }
>
> --
> 2.39.3
>
prev parent reply other threads:[~2026-10-14 14:35 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 10:58 [PATCH] ocfs2: validate the xattr tree root of an indexed xattr block Joseph Qi
2026-10-14 14:35 ` Heming Zhao [this message]
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=as-SUraepqsded7n@p15 \
--to=heming.zhao@suse.com \
--cc=akpm@linux-foundation.org \
--cc=jlbec@evilplan.org \
--cc=joseph.qi@linux.alibaba.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mark@fasheh.com \
--cc=ocfs2-devel@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.