lkml.org 
[lkml]   [2022]   [Sep]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.10 062/141] xfs: validate inode fork size against fork format
    Date
    From: Dave Chinner <dchinner@redhat.com>

    commit 1eb70f54c445fcbb25817841e774adb3d912f3e8 upstream.

    [backport for 5.10.y]

    xfs_repair catches fork size/format mismatches, but the in-kernel
    verifier doesn't, leading to null pointer failures when attempting
    to perform operations on the fork. This can occur in the
    xfs_dir_is_empty() where the in-memory fork format does not match
    the size and so the fork data pointer is accessed incorrectly.

    Note: this causes new failures in xfs/348 which is testing mode vs
    ftype mismatches. We now detect a regular file that has been changed
    to a directory or symlink mode as being corrupt because the data
    fork is for a symlink or directory should be in local form when
    there are only 3 bytes of data in the data fork. Hence the inode
    verify for the regular file now fires w/ -EFSCORRUPTED because
    the inode fork format does not match the format the corrupted mode
    says it should be in.

    Signed-off-by: Dave Chinner <dchinner@redhat.com>
    Reviewed-by: Christoph Hellwig <hch@lst.de>
    Reviewed-by: Darrick J. Wong <djwong@kernel.org>
    Signed-off-by: Dave Chinner <david@fromorbit.com>
    Signed-off-by: Amir Goldstein <amir73il@gmail.com>
    Acked-by: Darrick J. Wong <djwong@kernel.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    fs/xfs/libxfs/xfs_inode_buf.c | 35 ++++++++++++++++++++++++++---------
    1 file changed, 26 insertions(+), 9 deletions(-)

    --- a/fs/xfs/libxfs/xfs_inode_buf.c
    +++ b/fs/xfs/libxfs/xfs_inode_buf.c
    @@ -358,19 +358,36 @@ xfs_dinode_verify_fork(
    int whichfork)
    {
    uint32_t di_nextents = XFS_DFORK_NEXTENTS(dip, whichfork);
    + mode_t mode = be16_to_cpu(dip->di_mode);
    + uint32_t fork_size = XFS_DFORK_SIZE(dip, mp, whichfork);
    + uint32_t fork_format = XFS_DFORK_FORMAT(dip, whichfork);

    - switch (XFS_DFORK_FORMAT(dip, whichfork)) {
    + /*
    + * For fork types that can contain local data, check that the fork
    + * format matches the size of local data contained within the fork.
    + *
    + * For all types, check that when the size says the should be in extent
    + * or btree format, the inode isn't claiming it is in local format.
    + */
    + if (whichfork == XFS_DATA_FORK) {
    + if (S_ISDIR(mode) || S_ISLNK(mode)) {
    + if (be64_to_cpu(dip->di_size) <= fork_size &&
    + fork_format != XFS_DINODE_FMT_LOCAL)
    + return __this_address;
    + }
    +
    + if (be64_to_cpu(dip->di_size) > fork_size &&
    + fork_format == XFS_DINODE_FMT_LOCAL)
    + return __this_address;
    + }
    +
    + switch (fork_format) {
    case XFS_DINODE_FMT_LOCAL:
    /*
    - * no local regular files yet
    + * No local regular files yet.
    */
    - if (whichfork == XFS_DATA_FORK) {
    - if (S_ISREG(be16_to_cpu(dip->di_mode)))
    - return __this_address;
    - if (be64_to_cpu(dip->di_size) >
    - XFS_DFORK_SIZE(dip, mp, whichfork))
    - return __this_address;
    - }
    + if (S_ISREG(mode) && whichfork == XFS_DATA_FORK)
    + return __this_address;
    if (di_nextents)
    return __this_address;
    break;

    \
     
     \ /
      Last update: 2022-09-26 13:02    [W:2.880 / U:1.052 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site