Re: [PATCH] ocfs2: validate inline xattr header before listxattr walks it

ZhengYuan Huang <[email protected]>
Newsgroups dev.linux.lists.ocfs2-devel,org.kernel.vger.linux-kernel
Message-ID <CAOmEq9ULZ3H5So+4vku2Zu3B8Mgt9kp-Vck_g66KOjWTAzv0VA@mail.gmail.com>
On Wed, Apr 29, 2026 at 10:44 AM ZhengYuan Huang <[email protected]> wrote:
>
> [BUG]
> listxattr() can walk off the end of a dinode block when the inode-body
> xattr header is corrupted:
>
> BUG: KASAN: use-after-free in ocfs2_xattr_list_entry+0x1bd/0x370 fs/ocfs2/xattr.c:918
> Read of size 13 at addr ffff88800ab5c0c0 by task syz.0.1231/3756
>
> Call Trace:
>  ...
>  ocfs2_xattr_list_entry+0x1bd/0x370 fs/ocfs2/xattr.c:918
>  ocfs2_xattr_list_entries+0x1e1/0x320 fs/ocfs2/xattr.c:938
>  ocfs2_xattr_ibody_list fs/ocfs2/xattr.c:982 [inline]
>  ocfs2_listxattr+0x4fb/0x980 fs/ocfs2/xattr.c:1044
>  vfs_listxattr+0xb4/0x120 fs/xattr.c:493
>  listxattr+0x76/0x170 fs/xattr.c:924
>  filename_listxattr fs/xattr.c:958 [inline]
>  path_listxattrat+0x137/0x320 fs/xattr.c:988
>  __do_sys_listxattr fs/xattr.c:1001 [inline]
>  ...
>
> [CAUSE]
> ocfs2_xattr_ibody_list() computes the inline xattr header from
> di->i_xattr_inline_size and passes it straight to
> ocfs2_xattr_list_entries(). If corruption inflates xh_count, the list
> walk steps past the inline xattr area and eventually past the end of the
> 4K dinode block. The xe_type load in ocfs2_xattr_get_type() then reads
> poisoned memory.
>
> [FIX]
> Validate di->i_xattr_inline_size before locating the header, then bound
> xh_count by the number of ocfs2_xattr_entry records that fit inside the
> claimed inline area. Reject corrupted metadata with ocfs2_error() and
> -EFSCORRUPTED instead of iterating past the dinode block.
>
> Signed-off-by: ZhengYuan Huang <[email protected]>
> ---
>  fs/ocfs2/xattr.c | 22 ++++++++++++++++++++++
>  1 file changed, 22 insertions(+)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@
>  static int ocfs2_xattr_ibody_list(struct inode *inode,
>                                   struct ocfs2_dinode *di,
>                                   char *buffer,
>                                   size_t buffer_size)
>  {
>         struct ocfs2_xattr_header *header = NULL;
>         struct ocfs2_inode_info *oi = OCFS2_I(inode);
>         int ret = 0;
> +       u16 xattr_count;
> +       size_t max_entries;
> +       u16 inline_size;
>
>         if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
>                 return ret;
>
> +       inline_size = le16_to_cpu(di->i_xattr_inline_size);
> +
> +       if (inline_size > inode->i_sb->s_blocksize ||
> +           inline_size < sizeof(struct ocfs2_xattr_header)) {
> +               ocfs2_error(inode->i_sb,
> +                           "Invalid xattr inline size %u in inode %llu\n",
> +                           inline_size,
> +                           (unsigned long long)OCFS2_I(inode)->ip_blkno);
> +               return -EFSCORRUPTED;
> +       }
> +
>         header = (struct ocfs2_xattr_header *)
>                  ((void *)di + inode->i_sb->s_blocksize -
> -                le16_to_cpu(di->i_xattr_inline_size));
> +                inline_size);
> +
> +       xattr_count = le16_to_cpu(header->xh_count);
> +       max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
> +                     sizeof(struct ocfs2_xattr_entry);
> +       if (xattr_count > max_entries) {
> +               ocfs2_error(inode->i_sb,
> +                           "Invalid xattr entry count %u (max %zu) in inode %llu\n",
> +                           xattr_count, max_entries,
> +                           (unsigned long long)OCFS2_I(inode)->ip_blkno);
> +               return -EFSCORRUPTED;
> +       }
>
>         ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
>
>         return ret;
>  }
> --
> 2.43.0

This bug has already been fixed by commit 2a4f33430e96. Sorry for the noise.

Thanks,
ZhengYuan Huang
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.